荷兰国旗问题,给定一个数组,将小于num的数放在左边,等于num的数放在中间,大于num的数放在右边
code:
1 void hlflags(int *l, int num, int size)//第一个参数是一个数组,第二个参数是比较值num,第三个参数是数组的长度 2 { 3 int p1 = -1, p2 = size; //下标小于等于p1都是小于num的数字,p2相反 4 int index = 0; 5 for (int i = 0; i < 10; i++) 6 { 7 cout << l[i] << " "; 8 } 9 cout << endl; 10 while (index != p2) 11 { 12 if (l[index] == num) 13 { 14 index++; 15 } 16 else if (l[index] < num) 17 { 18 int item = l[index]; 19 l[index] = l[++p1]; 20 l[p1] = item; 21 index++; //小于的时候这个下标也要加1 22 } 23 else 24 { 25 int item = l[index]; 26 l[index] = l[--p2]; 27 l[p2] = item; 28 } 29 cout << "index:" << index << " p1:" << p1 << " p2:" << p2 << endl; 30 } 31 }
理解了荷兰国旗问题之后我们就可以使用荷兰国旗的思想来实现快速排序:
经典快排:
1 void quicksort(int *arr, int l, int r)//第一个参数是数组,第二个参数是左边界,第三个参数是右边界 2 { 3 if (l < r) 4 { 5 int a = partition(arr, l, r); 6 quicksort(arr, l, a - 1); 7 quicksort(arr, a + 1, r); 8 } 9 } 10 11 int partition(int *arr, int l, int r) 12 { 13 int less = l - 1; 14 int more = r; 15 while(l < more) 16 { 17 if (arr[l] < arr[r]) 18 { 19 swap(arr[l++], arr[++less]); 20 } 21 else if (arr[l] > arr[r]) 22 { 23 swap(arr[l], arr[--more]); 24 } 25 else 26 { 27 l++; 28 } 29 } 30 swap(arr[r], arr[more]); 31 return more; 32 }
经典快速排序每次递归只能解决一个数,我们可以对做一下改进,每次递归的时候返回一个数组区间,该区间内的数的大小等于被比较的数
改进后的快速排序:
1 void quicksort(int *arr, int l, int r) 2 { 3 if (l < r) 4 { 5 int *a = partition(arr, l, r); 6 quicksort(arr, l, a[0]); 7 quicksort(arr, a[1]+1, r); 8 delete[] a; 9 } 10 } 11 12 int* partition(int *arr, int l, int r) 13 { 14 int less = l - 1; 15 int more = r; 16 while(l < more) 17 { 18 if (arr[l] < arr[r]) 19 { 20 swap(arr[l++], arr[++less]); 21 } 22 else if (arr[l] > arr[r]) 23 { 24 swap(arr[l], arr[--more]); 25 } 26 else 27 { 28 l++; 29 } 30 } 31 swap(arr[r], arr[more]); 32 int *a = new int[2]; 33 a[0] = less; 34 a[1] = more; 35 return a; 36 }
上面两种快速排序由于被比较的数每次都是最后一个,所以以上两种快速排序和数据状况有关系,所以我们可以采用随机数的方式每次随机选择被比较的数
随机快速排序:
1 void quicksort(int *arr, int l, int r) 2 { 3 if (l < r) 4 { 5 int *a = partition(arr, l, r); 6 quicksort(arr, l, a[0]); 7 quicksort(arr, a[1]+1, r); 8 delete[] a; 9 } 10 } 11 12 int* partition(int *arr, int l, int r) 13 { 14 int less = l - 1; 15 int more = r; 16 17 srand(time(NULL)); 18 int t = int(rand()) % (r - l + 1) + l; 19 swap(arr[t], arr[r]); 20 //这三行代码就实现了随机快速排序 21 while(l < more) 22 { 23 if (arr[l] < arr[r]) 24 { 25 swap(arr[l++], arr[++less]); 26 } 27 else if (arr[l] > arr[r]) 28 { 29 swap(arr[l], arr[--more]); 30 } 31 else 32 { 33 l++; 34 } 35 } 36 swap(arr[r], arr[more]); 37 int *a = new int[2]; 38 a[0] = less; 39 a[1] = more; 40 return a; 41 }
根据期望值,这种随机快速排序的时间复杂度是 log(n)*n ,额外空间复杂度是 log(n).
接下来讲一下堆排序,一个堆就是一个完全二叉树,不了解二叉树没有关系,我们可以使用数组来模拟一颗二叉树,直接上代码:
堆排序:
1 void heapinsert(int *arr, int len) //数组 数组的长度 作用数形成一个大根堆 2 { 3 for (int i = 1; i < len; i++) 4 { 5 int index = i; 6 while (index > 0) 7 { 8 if (arr[index] > arr[(index - 1) / 2]) 9 { 10 swap(arr[index], arr[(index - 1) / 2]); 11 index = (index - 1) / 2; 12 continue; 13 } 14 break; 15 } 16 } 17 } 18 19 void heapify(int *arr, int index,int heapsize) //数组,数组中哪个数不满足大根堆的下标 数组长度 20 { 21 int left = index * 2 + 1; 22 while (left < heapsize) 23 { 24 int largest = left + 1 < heapsize&&arr[left + 1] > arr[left] ? left + 1 : left; 25 largest = arr[index] > arr[largest] ? index : largest; 26 if (largest == index) 27 { 28 break; 29 } 30 swap(arr[index], arr[largest]); 31 index = largest; 32 left = index * 2 + 1; 33 } 34 } 35 36 void heapsort(int *arr, int len) //数组 数组长度 37 { 38 if (len <= 2) 39 { 40 return; 41 } 42 heapinsert(arr, len); 43 swap(arr[0], arr[--len]); 44 while (len >= 1) 45 { 46 heapify(arr, 0, len); 47 swap(arr[0], arr[--len]); 48 } 49 }