笔试算法题(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
原文地址:https://www.cnblogs.com/leo-chen-2014/p/3730551.html