数据结构学习第二十三天
13:50:24 2019-09-14
继续把未看完的看完
13:19:50 2019-09-15
补上了归并的非递归算法
16:10:24 2019-09-17
补上了 堆排序的精明版本
排序算法
定理:任意$N$个不同元素组成的序列平均具有$N(N-1)/4$个逆序对
定理:任何仅以交换相邻两元素来排序的算法,其平均时间复杂度为$Ω(N^2)$
这样子 冒泡排序 插入排序 的最坏情况都是 N^2
要使排序算法变高效 得使用间隔元素来交换 在一次排序中 交换多个逆序对的元素
希尔排序
定义增量序列$D_M>D_{M-1}>...>D_1=1$
$对每个D_k进行“D_k-间隔”排序(k=M,M-1,...1)$
$注意:“D_k-间隔"有序的序列,在执行”D_{k-1}间隔“排序后,仍然是“D_k-间隔"有序的$
如果增量元素不互质,则小增量可能根本不起作用
比如 $N=N/2$ 最坏情况 时候 $T=\theta(N^{2})$
那么就会有其他的增量序列:
Hibbard增量序列 $D_k=2^k-1$ --相邻元素互质 最坏情况$T=\theta(N^{3/2})$
猜想 Hibbard增量的 平均复杂度 $T_{avg}=O(N^{5/4})$
Sedgewick增量序列 $ \{1,5,19,41,109,.....\}$
$9*4^i-9*2^i或4^i-3*2^i+1$猜想$T_{avg}=O(N^{7/6}) T_{worst}=O(N^{4/3})$
归并排序的时间复杂度为$O(NlogN)$ 该算法是稳定的
冒泡排序 插入排序 希尔排序 选择排序 堆排序 归并排序(递归与非递归) (ps:其中堆排序写的是憨憨版本 之后会添上精明版本)
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<stdio.h> 3 #include<malloc.h> 4 void Print(int A[], int N) 5 { 6 for (int i = 0; i < N; i++) 7 printf("%d ", A[i]); 8 printf("\n"); 9 } 10 //排序算法 11 12 //冒泡排序 //最好 O(N) 最坏 O(N^2) 算法稳定 13 void Swap(int A[], int i, int j) 14 { 15 int temp = A[i]; 16 A[i] = A[j]; 17 A[j] = temp; 18 } 19 void Bubble_Sort(int A[], int N) 20 { 21 for (int i = 0; i < N; i++) 22 { 23 int IsSwap = 0; 24 for (int j = 0; j < N - i-1; j++) 25 { 26 if (A[j] > A[j + 1]) 27 { 28 IsSwap = 1; 29 Swap(A, j, j + 1); 30 } 31 } 32 if (!IsSwap) 33 break; 34 } 35 } 36 37 //插入排序 //最好O(N) 最坏 O(N^2) 算法稳定 38 void Insert(int A[], int j, int i) 39 { 40 int temp = A[i]; 41 for (; i > j; i--) 42 A[i] = A[i - 1]; 43 A[j] = temp; 44 } 45 void Insert_Sort(int A[], int N) 46 { 47 /*for (int i = 0; i < N-1; i++) 48 { 49 int m = i + 1; 50 int j = i; 51 while (j >= 0 && A[j] > A[m]) 52 j--; 53 Insert(A, j+1, m); 54 }*/ 55 /*for (int i = 1; i < N; i++) 56 { 57 int temp = A[i]; 58 int j; 59 for (j = i - 1; j >= 0 && A[j] > temp; j--) 60 A[j + 1] = A[j]; 61 A[j + 1] = temp; 62 }*/ 63 for (int p = 1; p < N; p++) 64 { 65 int temp = A[p]; 66 int i; 67 for (i = p; i > 0 && A[i - 1] > temp; i--) 68 A[i] = A[i - 1]; 69 A[i] = temp; 70 } 71 } 72 73 //希尔排序 //最坏情况 //改进插入排序 74 void Shell_Sort(int A[], int N) 75 { 76 /*for (int D = N / 2; D > 0; D /= 2) 77 { 78 for (int p = D; p < N; p++) 79 { 80 int temp = A[p]; 81 int i; 82 for (i = p; i >= D && A[i - D] > temp; i -= D) 83 A[i] = A[i - D]; 84 A[i] = temp; 85 } 86 }*/ 87 // 利用Sedgewick增量序列 88 int Si; 89 int Sedgewick[] = { 929,505,209,109,41,19,5,1,0 }; 90 for (Si = 0; Sedgewick[Si] >=N; Si++) //初始的增量Sedgewick[Si]不能超过待排序序列长度 91 ; 92 for (int D = Sedgewick[Si]; D>0;D=Sedgewick[++Si]) 93 { 94 for (int p = D; p < N; p++) 95 { 96 int temp = A[p]; 97 int i; 98 for (i = p; i >= D && A[i - D] > temp; i -= D) 99 A[i] = A[i - D]; 100 A[i] = temp; 101 } 102 } 103 } 104 105 //选择排序 106 void Selection_Sort(int A[], int N) 107 { 108 for (int i = 0; i < N; i++) 109 { 110 int MinPosition = i; 111 int Min = A[i]; 112 int j; 113 for (j = i + 1; j < N; j++) 114 { 115 if (A[j] < Min) 116 { 117 MinPosition = j; 118 Min = A[j]; 119 } 120 } 121 Swap(A, i, MinPosition); 122 } 123 } 124 125 //堆排序 //选择排序的一种改进 126 //改进了寻找最小元的算法 利用最小堆来实现 127 typedef struct HeapStruct* MinHeap; 128 struct HeapStruct 129 { 130 int* Elements; 131 int Size; 132 int Capacity; 133 }; 134 MinHeap Create(int MaxSize) 135 { 136 MinHeap H = (MinHeap)malloc(sizeof(struct HeapStruct)); 137 H->Elements = (int*)malloc(sizeof(int) * (MaxSize + 1)); 138 H->Capacity = MaxSize; 139 H->Size = 0; 140 H->Elements[0] = -10001; 141 return H; 142 } 143 MinHeap H = Create(20); 144 //建立最小堆 145 void PrecDown(MinHeap H, int i) //下滤 146 { 147 int Parent, Child; 148 int Tmp = H->Elements[i]; 149 for (Parent = i; Parent * 2 <= H->Size; Parent = Child) 150 { 151 Child = Parent * 2; 152 if ((Child != H->Size) && (H->Elements[Child] > H->Elements[Child + 1])) 153 Child++; 154 if (Tmp <= H->Elements[Child])break; 155 else 156 H->Elements[Parent] = H->Elements[Child]; 157 } 158 H->Elements[Parent] = Tmp; 159 } 160 void BuildMinHeap(MinHeap H) 161 { 162 int i; 163 for (i = H->Size / 2; i > 0; i--) 164 PrecDown(H, i); 165 } 166 int DeleteMin(MinHeap H) 167 { 168 int Parent, Child; 169 int MinItem, temp; 170 MinItem = H->Elements[1]; 171 temp = H->Elements[H->Size--]; 172 for (Parent = 1; Parent * 2 <= H->Size; Parent = Child) 173 { 174 Child = Parent * 2; 175 if (Child != H->Size && H->Elements[Child] > H->Elements[Child + 1]) 176 Child++; 177 if (H->Elements[Child] >= temp)break; 178 else 179 H->Elements[Parent] = H->Elements[Child]; 180 } 181 H->Elements[Parent] = temp; 182 return MinItem; 183 } 184 185 //利用最大堆实现堆排序 (升序) 186 void Precdown(int A[], int i, int N) 187 { 188 int Parent, Child; 189 int Tmp; 190 Tmp = A[i]; 191 for (Parent = i; (Parent * 2) + 1 <= N; Parent = Child) 192 { 193 Child = (Parent * 2) + 1; 194 if (Child != N && A[Child] < A[Child + 1]) 195 Child++; 196 if (A[Child]<=Tmp)break; 197 else 198 A[Parent] = A[Child]; 199 } 200 A[Parent] = Tmp; 201 } 202 void BuildMaxHeap(int A[], int N) 203 { 204 for (int i = (N - 1) / 2; i >= 0; i--) 205 Precdown(A, i, N - 1); 206 } 207 208 void Heap_Sort(int A[], int N) 209 { 210 //算法一 利用最小堆 211 /*int Tmp[10] = { 0 }; 212 for (int i = 0; i < 10; i++) 213 Tmp[i] = DeleteMin(H); 214 for (int i = 0; i < 10; i++) 215 A[i] = Tmp[i];*/ 216 217 /*for (int i = 0; i < 10; i++) 218 A[i] = DeleteMin(H);*/ 219 //算法二 利用最大堆后 再调整 220 BuildMaxHeap(A, N); 221 //之后调整最大堆 222 for (int i = N - 1; i >= 0; i--) 223 { 224 Swap(A, 0, i); 225 Precdown(A, 0, i - 1); 226 } 227 } 228 229 //归并排序 230 //递归算法 231 void Merge(int A[],int lo,int mi,int hi) 232 { 233 //两个有序序列的合并 234 int* B = (int*)malloc(sizeof(int) * (mi - lo)); 235 for (int i = 0; i < mi - lo;i++)B[i] = A[lo + i]; 236 int i, j, k; 237 i = lo; 238 j = 0; 239 k = mi; 240 /*while (j<mi-lo||k<hi) 241 { 242 if((j<mi-lo)&&(k>=hi||B[j]<=A[k]))A[i++]=B[j++]; 243 if ((k < hi) && (j >= mi-lo|| B[j] > A[k]))A[i++] = A[k++]; 244 }*/ 245 //化简版 246 while (j<mi-lo) 247 { 248 if (k >= hi || B[j] <= A[k])A[i++] = B[j++]; 249 if (k<hi && B[j]>A[k])A[i++] = A[k++]; 250 } 251 /*邓公版本*/ 252 /*int* A1= A + lo; 253 int* B = (int*)malloc(sizeof(int) * (mi - lo)); 254 for (int i = 0; i < mi - lo; B[i] = A1[i++]); 255 int* C = A + mi; 256 int i, j, k; 257 for (i = j = k = 0; j < mi - lo || k < hi-mi;) 258 { 259 if (j < mi - lo&&((k >= hi - mi)||B[j] <=C[k]))A1[i++] = B[j++]; 260 if (k < hi - mi && (j >= mi - lo || B[j] > C[k]))A1[i++] = C[k++]; 261 }*/ 262 //简化 263 /*for (i = j = k = 0; j < mi - lo;) 264 { 265 if (k >= hi - mi || B[j] <= C[k])A1[i++] = B[j++]; 266 if (k < hi - mi && (j >= mi - lo || B[j] > C[k]))A1[i++] = C[k++]; 267 }*/ 268 free(B); 269 } 270 void MergeSort(int A[],int lo,int hi) 271 { 272 if (lo == hi - 1) 273 return; 274 int mi = (lo + hi) >> 1; 275 MergeSort(A,lo, mi); 276 MergeSort(A, mi, hi); 277 Merge(A,lo, mi, hi); 278 } 279 //非递归算法 280 void Merge_pass(int A[],int N, int length) //length为当前有序子列的长度 281 { 282 int i; 283 for (i = 0; i <= N - 2 * length;i += 2 * length) 284 Merge(A,i, i + length, i + 2 * length); 285 if (i+length< N) //如果最后有两个子列 286 Merge(A, i, i+length, N); 287 } 288 void Merge_Sort(int A[], int N) 289 { 290 int length = 1; 291 while (length<=N) 292 { 293 Merge_pass(A, N, length); 294 length *= 2; 295 } 296 } 297 298 int main() 299 { 300 int A[10]; 301 for (int i = 0; i < 10; i++) 302 scanf("%d", &A[i]); 303 //Bubble_Sort(A, 10); //冒泡排序 304 //Insert_Sort(A, 10); //插入排序 305 //Shell_Sort(A, 10); //希尔排序 306 //Selection_Sort(A, 10); //选择排序 307 308 //堆排序 //最小堆之憨憨算法 309 /*for (int i = 1; i <= 10; i++) 310 H->Elements[++H->Size] = A[i - 1]; 311 BuildMinHeap(H); //构建最小堆 312 Heap_Sort(A, 10);*/ 313 314 //堆排序 //最大堆之精比算法 315 Heap_Sort(A, 10); 316 317 //MergeSort(A,0,10) //归并排序递归算法 318 //Merge_Sort(A,10); //归并排序非递归算法 319 Print(A, 10); 320 return 0; 321 }
快速排序:是排序算法中最快的 但要注意实现的细节(对于大规模数据来说)
首先要注意主元的选取 对主元的选取会影响到算法的时间复杂度 还有就是快速排序是利用递归实现的 对于小规模数据 可能整体时间不入插入排序快 因此可以使用一个$Cutoff$ 当数据规模过小时 不使用快排 而使用 插入排序
下面会给出对快速排序的测试
表排序
不移动元素本身 而移动指针的 排序 叫做间接排序
利用一个Table数组来对下标进行排序
物理排序
N个数字的排列由若干个独立的环组成
桶排序
基数排序
用“次位优先" (Least Significant Digit)
"主位优先"(Most Significant Digit)
对排序的测试
冒泡排序: 可以看出当数据量过大时 运行超时
插入排序:虽然所有数据都能排序,但当数据量过大时,耗时增加
希尔排序(Sedgewick):希尔排序快了不少
选择排序:答案正确,耗时却不少
堆排序(憨憨版本):速度也很快
堆排序(精明版本): 速度也挺好
归并排序(递归):也很快 但占的内存却很大 比前面的排序大了4、5倍 经检查 是因为没有在归并算法里free掉申请的空间
每次申请完空间后释放掉 内存占用就少了许多
归并排序(非递归) :看起来内存占用并没有少 一方面是因为我没有用陈越姥姥的方法去写递归与非递归的排序 更重要的是开辟的数组空间很大 但也可以看出 非递归算法比递归快一些
对快速排序的测试:
对pivot进行测试(此时不使用Cutoff来限定范围):
①pivot采取A[0] 有三个数据点耗时较多
②pivot采取A[mi] 速度不错
③pivot采取 三元选中值 某个数据点慢了些 其他都挺快的
④采用随机函数rand() 速度也行 不知道改一改获取rand值的函数会不会更快点
对Cutoff进行测试(此时选取pivot为A[mi]):
①Cutoff取值为50
②Cutoff取100
③Cutoff取200
④Cutoff取300
⑤Cutoff取400
PTA第27题 简单的一道桶排序题
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<stdio.h> 3 #include<malloc.h> 4 #include<stdlib.h> 5 int Bucket[55]; 6 int main() 7 { 8 int N,Num; 9 scanf("%d", &N); 10 for (int i = 0; i < N; i++) 11 { 12 scanf("%d", &Num); 13 Bucket[Num]++; 14 } 15 int count = 0; 16 for (int i = 0; i < 55; i++) 17 { 18 if (Bucket[i]) 19 { 20 printf("%d:%d\n", i, Bucket[i]); 21 count++; 22 } 23 } 24 }
PTA 第25题 根据给的2串数组 判断是 插入排序还是 归并排序
我是用归并的想法 不断检查以Length为长度的数组 判断是否满足是归并的最小条件(Length=2时满足)
如果满足 就继续向下查 更新之后需要的长度L 不是就返回去做下一步操作
(os:这是PTA 2014年冬季考试题...我做了有一个小时四十分钟。。这还不算昨天思考的时间。。这要去考试不得凉凉)
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<stdio.h> 3 #include<malloc.h> 4 #include<stdlib.h> 5 int A[110]; 6 int B[110]; 7 int L; 8 void Print(int N) 9 { 10 int i; 11 for (i = 0; i < N - 1; i++) 12 printf("%d ", B[i]); 13 printf("%d", B[i]); 14 } 15 int IsOrder(int B[],int lo, int hi) 16 { 17 for (int i = lo; i < hi - 1; i++) 18 if (B[i] > B[i + 1]) 19 return 0; 20 return 1; 21 } 22 int ChargeLength(int B[], int N, int Length) //检查以Length为长度的序列 23 { 24 int i; 25 for (i = 0; i < N -Length; i +=Length) 26 if (!IsOrder(B, i, i+Length)) 27 return 0; 28 if (i< N) 29 if (!IsOrder(B, i, N)) 30 return 0; 31 L = Length; 32 return 1; 33 } 34 int Charge(int B[], int N) 35 { 36 int Length = 2; 37 while(Length <=N) 38 { 39 if (ChargeLength(B, N, Length)) 40 Length *= 2; 41 else 42 if (Length > 2) 43 return 1; 44 else 45 return 0; 46 } 47 } 48 49 void Insert_Once(int B[],int i,int N) 50 { 51 int j; 52 int Temp = B[i]; 53 for (j = i; j > 0 && B[j - 1] >Temp; j--) 54 B[j] = B[j - 1]; 55 B[j] = Temp; 56 } 57 58 void Merge(int lo, int mi, int hi) 59 { 60 int* B1 = (int*)malloc(sizeof(int) * (mi - lo)); 61 for (int i = 0; i < mi - lo; i++)B1[i] = B[lo + i]; 62 int i, j, k; 63 i = lo; 64 j = 0; 65 k = mi; 66 while (j < mi - lo) 67 { 68 if (k >= hi || B1[j] <= B[k])B[i++] = B1[j++]; 69 if (k<hi && B1[j]>B[k])B[i++] = B[k++]; 70 } 71 free(B1); 72 } 73 void Merge_Once(int B[], int Length, int N) 74 { 75 int i; 76 for (i = 0; i < N - 2 * Length; i += 2 * Length) 77 Merge(i, i + Length, i + 2 * Length); 78 if (i + Length < N) 79 Merge(i, i + Length, N); 80 } 81 82 int main() 83 { 84 int N; 85 int Flag = 0; 86 int OrderPosition = 0; 87 scanf("%d", &N); 88 for (int i = 0; i < N; i++) 89 scanf("%d", &A[i]); 90 for (int i = 0; i < N; i++) 91 scanf("%d", &B[i]); 92 if (Flag=Charge(B, N)) 93 printf("Merge Sort\n"); 94 else 95 printf("Insertion Sort\n"); 96 for (int i = 0; i < N - 1; i++) 97 if (B[i] <=B[i + 1]) //这里必须是大于等于 98 OrderPosition = i + 1; 99 else 100 break; 101 if (Flag) 102 Merge_Once(B, L, N); 103 else 104 Insert_Once(B, OrderPosition+1, N); 105 Print(N); 106 return 0; 107 }
PTA 第26题 根据给的两串数组 判断是 插入排序还是 堆排序
观察数据可以发现是利用 精明算法来 实现堆排序
判断是 堆排序还是 插入排序 我是直接看 前三个元素
这道题 给出的例子至少会经过一次排序 所以看前三个元素 就可以知道是什么排序了 简单来说 插入排序最早就把前两个元素排好序了 而 堆排序对前两个元素的排序是最后才排的
至于接下来做一步操作 插入排序很简单 堆排序我是直接把排序好的结果 与 给的 数据 进行比较 来找出 那个位置点
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<stdio.h> 3 #include<malloc.h> 4 #include<stdlib.h> 5 int A[110]; 6 int B[110]; 7 int L; 8 void Swap(int *C,int i, int j) 9 { 10 int temp = C[i]; 11 C[i] = C[j]; 12 C[j] = temp; 13 } 14 15 void Print(int N) 16 { 17 int i; 18 for (i = 0; i < N - 1; i++) 19 printf("%d ", B[i]); 20 printf("%d", B[i]); 21 } 22 23 int Charge(int N) 24 { 25 int i; 26 if (B[0] > B[1] && B[0] > B[2]) 27 return 1; 28 else 29 return 0; 30 } 31 32 void Insert_Once(int i, int N) 33 { 34 int j; 35 int Temp = B[i]; 36 for (j = i; j > 0 && B[j - 1] > Temp; j--) 37 B[j] = B[j - 1]; 38 B[j] = Temp; 39 } 40 41 void PrecDown(int i,int N) 42 { 43 int Parent, Child; 44 int Tmp = A[i]; 45 for (Parent = i; Parent * 2 + 1 <= N; Parent = Child) 46 { 47 Child = Parent * 2 + 1; 48 if (Child != N && A[Child] < A[Child + 1]) 49 Child++; 50 if (Tmp >= A[Child])break; 51 else 52 A[Parent] = A[Child]; 53 } 54 A[Parent] = Tmp; 55 } 56 57 void BuildMaxHeap(int N) 58 { 59 for (int i = (N - 1) / 2; i >= 0; i--) 60 PrecDown(i, N - 1); 61 } 62 63 void Heap_Sort(int N) 64 { 65 for (int i = N - 1; i >= 0; i--) 66 { 67 Swap(A, 0, i); 68 PrecDown(0, i-1); 69 } 70 } 71 72 int FindI(int N) 73 { 74 int i; 75 for (i = N - 1; i >= 0 && A[i] == B[i]; i--); 76 return i; 77 } 78 79 void PrecDown_Once(int i) 80 { 81 Swap(B,0, i); 82 int Parent, Child; 83 int tmp; 84 tmp = B[0]; 85 for (Parent = 0; (Parent * 2) + 1 <= i-1; Parent = Child) 86 { 87 Child = (Parent * 2) + 1; 88 if (Child != i-1&& B[Child]<B[Child + 1]) 89 Child++; 90 if (tmp >= B[Child])break; 91 else 92 B[Parent] = B[Child]; 93 } 94 B[Parent] = tmp; 95 } 96 97 void Heap_Once(int N) 98 { 99 BuildMaxHeap(N); 100 Heap_Sort(N); 101 int i = FindI(N); 102 PrecDown_Once(i); 103 } 104 105 int main() 106 { 107 int N; 108 int Flag = 0; 109 int OrderPosition = 0; 110 scanf("%d", &N); 111 for (int i = 0; i < N; i++) 112 scanf("%d", &A[i]); 113 for (int i = 0; i < N; i++) 114 scanf("%d", &B[i]); 115 if (Flag = Charge(N)) 116 printf("Heap Sort\n"); 117 else 118 printf("Insertion Sort\n"); 119 for (int i = 0; i < N - 1; i++) 120 if (B[i] <= B[i + 1]) //这里必须是大于等于 121 OrderPosition = i + 1; 122 else 123 break; 124 if (Flag) 125 Heap_Once(N); 126 else 127 Insert_Once(OrderPosition + 1, N); 128 Print(N); 129 return 0; 130 }