• 笔试算法题(03):最小第K个数 & 判定BST后序序列


    出题:输入N个整数,要求输出其中最小的K个数;

    分析:

    • 快速排序和最小堆都可以解决最小(大)K个数的问题(时间复杂度为O(NlogN));另外可以建立大小为K的最大堆,将前K个数不断插入最大堆,对于之后的N-K个数,依次与堆顶元素进行比较,如果新元素更小则删除当前堆顶元素,并更新最大堆;如果新元素大则跳过;
    • 第一种实现:快速排序框架,插入排序处理小子文件,随机函数选取split点,双向扫描;
    • 第二种实现:最小堆框架,基于fixUp的堆构建策略(数组整体更新,bottom up),通过删除前K个堆顶元素获取最小K个数;
    • 第三种实现:最大堆框架,基于fixDown的堆构建策略(0起点插入式,top down),首先构建K个数的最大堆,然后新元素与堆顶元素进行比较,小于则替换现有堆顶元素,更新最大堆;

    解题:

      1 #include <stdlib.h>
      2 #include <stdio.h>
      3 #include <malloc.h>
      4 #include <string.h>
      5 #include <time.h>
      6 
      7 void ShowMinK(int *array, int k) {
      8         printf("
    Min k: 
    ");
      9         for(int m=0; m<=k;m++) {
     10                 printf("%d, ",array[m]);
     11         }
     12 }
     13 
     14 void merge(int *array, int i, int split, int j) {
     15         /**
     16          * 此版本采用的是双向遍历,left和right指针同时从左右向中间遍历
     17          * 其他还有单向遍历和三相遍历,此处不作解释
     18          * */
     19         /**
     20          * 首先将split元素交换到j作为参考元素
     21          * */
     22         int temp=array[split];
     23         array[split]=array[j];
     24         array[j]=temp;
     25 
     26         int left=i; int right=j-1;
     27 
     28         while(true) {
     29                 /**
     30                  * 两个指针同时相向遍历,当指针相遇(交错)或者满足与array[j]
     31                  * 的特定关系,则暂时停止。
     32                  * */
     33                 while(left<right && array[left]<=array[j]) {
     34                         left++;
     35                 }
     36                 while(right>left && array[right]>array[j]) {
     37                         right--;
     38                 }
     39 
     40                 if(left>=right) break;
     41 
     42                 temp=array[left];
     43                 array[left]=array[right];
     44                 array[right]=temp;
     45                 left++;
     46                 right--;
     47         }
     48         /**
     49          * 将split元素从j位置交换回分界位置
     50          * */
     51         temp=array[left];
     52         array[left]=array[j];
     53         array[j]=temp;
     54 }
     55 
     56 void InsertSort(int *array, int i, int j) {
     57         /**
     58          * 此版本仅使用最基本的插入排序思想,左边的序列都已经排好序
     59          * 每一次循环都是讲array[m]插入左边序列
     60          * 其他版本可以将找到最大值并放到最左边,这样可以省去每次
     61          * 循环中对左边范围的检查
     62          * */
     63         int temp,k;
     64         for(int m=i+1;m<=j;m++) {
     65                 temp=array[m];
     66                 k=m-1;
     67                 while(k>=i) {
     68                         if(array[k]>temp) {
     69                                 /**
     70                                  * 当array[k]大于temp,则将其向前复制
     71                                  * */
     72                                 array[k+1]=array[k];
     73                         } else {
     74                                 /**
     75                                  * 当array[k]小于等于temp,则最终将m插入
     76                                  * array[k+1],此次插入结束
     77                                  * */
     78                                 break;
     79                         }
     80                         k--;
     81                 }
     82                 /**
     83                  * 注意不同的跳出条件,是从break跳出的还是k>=i跳出的
     84                  * */
     85                 array[k+1]=temp;
     86         }
     87 }
     88 
     89 void MinKQuickSort(int *array, int i, int j, int k) {
     90         /**
     91          * 当数组长度小于7的时候,直接的插入排序将会优于更小的子递归
     92          * */
     93 
     94         if(j-i+1 < 7) {
     95                 InsertSort(array, i, j);
     96                 ShowMinK(array, k);
     97                 return;
     98         }
     99 
    100         /**
    101          * 使用随机函数rand()选择split的策略,避免最坏情况
    102          * time()或者getpid()可以作为srand函数的参数
    103          * */
    104         if(i>j) return;
    105         if(i==j && i==k){
    106                         ShowMinK(array,k);
    107                         return;
    108                 }
    109         srand((int)time(0));
    110         int split=rand()%(j-i+1) + i;
    111         merge(array, i, split, j);
    112 
    113         if(split > k) {
    114                 /**
    115                  * 当前的split大于k,所以前k小的数应该在左边部分,k已经是相对i
    116                  * 的计数,所以需要减去i
    117                  * */
    118                 MinKQuickSort(array, i, split-1, k);
    119         } else if(split < k) {
    120                 /**
    121                  *
    122                  * 当前的split小于k,所以前k小 的数应该在右边部分,k是相对i的
    123                  * 计数,现在需要相对split+1,所以需要更改
    124                  * */
    125                 MinKQuickSort(array, split+1, j, k);
    126         } else if(split == k) {
    127                 ShowMinK(array, k);
    128         }
    129 }
    130 /**
    131  *
    132  * */
    133 void MinFixUp(int *array, int length, int i) {
    134         int start=i;
    135         int temp,father;
    136         while(start>1 && array[start]<array[start/2]) {
    137 
    138                 temp=array[father];
    139                 array[father]=array[start];
    140                 array[start]=temp;
    141                 start/=2;;
    142         }
    143 }
    144 
    145 void MinFixDown(int *array, int length, int i) {
    146         int s;
    147         int temp, father;
    148         while(2*i<=length) {
    149                 s=2*i;
    150                 if(s+1<=length && array[s] > array[s+1]) {
    151                         s++;
    152                 }
    153                 if(array[i]<=array[s]) break;
    154 
    155                 temp=array[i];
    156                 array[i]=array[s];
    157                 array[s]=temp;
    158                 i=s;
    159         }
    160 }
    161 
    162 /**
    163  * 从最后一个拥有子节点的内部节点开始逆向遍历到根节点
    164  * */
    165 void BuildMinHeap(int *array, int length) {
    166 
    167         for(int i=length/2;i>=1;i=i-1) {
    168                 MinFixDown(array, length, i);
    169         }
    170 }
    171 
    172 /**
    173  * 从array[1]开始存储元素,从而简化代码
    174  * 首先构建最小堆,然后删除堆顶元素,缩小堆大小
    175  * 更新堆结构,重复K次
    176  * */
    177 void MinKMiniHeap(int *array, int length, int k) {
    178         BuildMinHeap(array, length);
    179         int capability=length;
    180         for(int i=1;i<=k;i++) {
    181                 printf("%d, ",array[1]);
    182                 array[1]=array[capability];
    183                 capability--;
    184                 MinFixDown(array, capability, 1);
    185         }
    186 }
    187 
    188 void MaxFixUp(int *array, int length, int i) {}
    189 
    190 void MaxFixDown(int *array, int length, int i) {}
    191 
    192 void BuildMaxHeap(int *array, int k) {}
    193 
    194 void MinKMaxHeap(int *array, int length, int k) {
    195         BuildMaxHeap(array, k);
    196         for(int i=k+1;i<=length;i++) {
    197                 if(array[1]>array[i]) {
    198                         array[1]=array[i];
    199                         MaxFixDown(array, k, 1);
    200                 }
    201         }
    202 
    203         int capability=k;
    204         for(int i=1;i<=k;i++) {
    205                 printf("%d, ",array[1]);
    206                 array[1]=array[capability];
    207                 capability--;
    208                 MaxFixDown(array, capability, 1);
    209         }
    210 
    211 }
    View Code

    出题:输入一个整数数组,判断该数组是否给定二元查找树(Binary Search Tree)的部分后序(Post Order)遍历结果(给定二元查找树,判定整数数组是否在其中);

    分析:使用int *position作为对比位置记录

    解题:

     1 struct Node {
     2         int value;
     3         Node *left;
     4         Node *right;
     5 };
     6 /**
     7  * Binary Search Tree可能不是满树,所以有右子树不一定就有左子树
     8  * 仅当子树中完全找到整个序列才返回true,否则返回false,使用position
     9  * 记录当前的比较位置
    10  * */
    11 bool PostOrderCompare(Node *cur, int *array, int length, int *position) {
    12         bool isLeft=false, isRight=false;
    13         if(cur->left != NULL) {
    14                 /**
    15                  * 左子树存在时候的后序遍历
    16                  * */
    17                 isLeft=PostOrderCompare(cur->left, array, length, position);
    18                 if(!isLeft && cur->right != NULL) {
    19                         isRight=PostOrderCompare(cur->right, array, length, position);
    20                 }
    21         } else if(cur->right != NULL) {
    22                 /**
    23                  * 仅右子树存在时候的后序遍历
    24                  * */
    25                 isRight=PostOrderCompare(cur->right, array, length, position);
    26         }
    27 
    28         if(isLeft || isRight) return true;
    29         /**
    30          * 左右子树都还没有满足条件,处理当前节点
    31          * */
    32         printf("
    current node: %d
    ", cur->value);
    33         printf("
    current position value: %d
    ", array[*position]);
    34         if(cur->value == array[*position]) {
    35                 if(*position +1 == length) return true;
    36                 else {
    37                         /**
    38                          * 仅当当前值匹配的时候position才更新, 否则直接返回
    39                          * */
    40                         *position = *position+1;
    41                 }
    42         }
    43         return false;
    44 }
    View Code
  • 相关阅读:
    css伪类和伪对象
    多浏览器css兼容分析小结
    Hibernate3源码分析之hibernate.cfg.xml配置文件与SessionFactory类
    php示例代码之使用mysqli对象
    C#中Web Service身份验证
    未能转换为类型库。类型库导出程序在处理,时遇到了错误。错误: 找不到元素
    MS RDLC reportview 在 Chrome中不显示查询结果的原因
    C# 创建ActiveX
    How to rebuild performance counters on Windows Vista/Server2008/7/Server2008R2
    jqGrid中文说明文档——选项设置
  • 原文地址:https://www.cnblogs.com/leo-chen-2014/p/3730551.html
Copyright © 2020-2023  润新知