一、算法导论上的快速排序
void swap(int* value1, int* value2) { int temp = *value1; *value1 = *value2; *value2 = temp; }
void QuickSort(int* array, int low, int high) { if (low >= high) return; int i = low - 1; for (int j = low; j < high; ++j) { if (array[j] < array[high]) { ++i; swap(&array[i], &array[j]); } } int pivot = i + 1; swap(&array[pivot], &array[high]); QuickSort(array, low, pivot - 1); QuickSort(array, pivot + 1, high); }
上述将一次快速排序的过程直接写在快速排序中,减少递归时调用函数的次数,提高效率。
非递归形式:
int Partition(int* array, int low, int high) {//a trip quicksort int i = low - 1; for (int j = low; j < high; ++j) { if (array[j] < array[high]) { ++i; swap(&array[i], &array[j]); } } int pivot = i + 1; swap(&array[pivot], &array[high]); return pivot; } void QuickSort(int* array, int low, int high) { //stack initilization int stack[200] = {0}; int top = 0; stack[top] = low; ++top; stack[top] = high; ++top; int start = 0; int end = 0; int pivot = 0; while (top) { start = stack[top]; --top; end = stack[top]; --top; pivot = Partition(array, start, end); if (start < pivot - 1) { stack[top] = start; ++top; stack[top] = pivot - 1; ++top; } if (pivot + 1 < end) { stack[top] = pivot + 1; ++top; stack[top] = end; ++top; } } }
递归算法使用的栈由程序自动产生,栈中包含:函数调用时的参数和函数中的局部变量。如果局部变量很多或者函数内部又调用了其他函数,则栈会很大。每次递归 调用都要操作很大的栈,效率自然会下降。而对于非递归算法,每次循环使用自己预先创建的栈,因此不管程序复杂度如何,都不会影响程序效率。一般情况下,同一个问题,非递归总比递归效率高。
二、Hoare及其变种版本的快速排序
Hoare版本:
void QuickSort(int* array, int low, int high) { if (low >= high) return; int key = array[low];//pivot int i = low; int j = high; while (i < j) { while (array[j] >= key && i < j) { --j; } while (array[i] <= key && i < j) { ++i; } if (i < j) { swap(&array[i], &array[j]); } } swap(&array[low], &array[i]); QuickSort(array, low, i - 1); QuickSort(array, i + 1, high); }
Hoare变种版本:
void QuickSort(int* array, int low, int high) { if (low >= high) return; int key = array[low]; int i = low; int j = high; while (i < j) { while (array[j] >= key && i < j) { --j; } if (i < j) { array[i] = array[j]; } while (array[i] <= key && i < j) { ++i; } if (i < j) { array[j] = array[i]; } } array[i] = key; QuickSort(array, low, i - 1); QuickSort(array, i + 1, high); }
该变种版本与算法导论版本比较:
①算法导论版本中使用swap来交换数据,而在Hoare变种版本中使用的是赋值的方式,其实这两种方式的效率没有多大差别。
②变种版本swap之后还要原地判断一次,但是算法导论版本不用。
③ 另外,小于等于的效率和小于的效率也是差不多的。
三、随机化快速排序
快速排序在最坏情况下,时间复杂度为O(n^2),这和主元的选择有关,通过随机化选择主元,减少最坏情况的发生。
递归形式:
void RandomizedQuickSort(int* array, int low, int high) { if (low >= high) return; //select pivot randomly int pivot = 0; srand(time(0)); pivot = low + rand()%(high - low + 1); swap(&array[pivot], &array[high]); int i = low - 1; for (int j = low; j < high; ++j) { if (array[j] < array[high]) { ++i; swap(&array[i], &array[j]); } } pivot = i + 1; swap(&array[pivot], &array[high]); QuickSort(array, low, pivot - 1); QuickSort(array, pivot + 1, high); }
非递归形式:
int Partition(int* array, int low, int high) {//a trip quicksort int i = low - 1; for (int j = low; j < high; ++j) { if (array[j] < array[high]) { ++i; swap(&array[i], &array[j]); } } int pivot = i + 1; swap(&array[pivot], &array[high]); return pivot; } void RandomizedQuickSort(int* array, int low, int high) { //stack initilization int stack[200] = {0}; int top = 0; stack[top] = low; ++top; stack[top] = high; ++top; int start = 0; int end = 0; int pivot = 0; while (top) { start = stack[top]; --top; end = stack[top]; --top;
//select pivot randomly srand(time(0)); pivot = start + rand()%(end - start + 1); swap(&array[pivot], &array[end]); pivot = Partition(array, start, end); if (start < pivot - 1) { stack[top] = start; ++top; stack[top] = pivot - 1; ++top; } if (pivot + 1 < end) { stack[top] = pivot + 1; ++top; stack[top] = end; ++top; } } }
四、平衡快速排序
选取头,尾,中间三个元素中中间值的元素来作为主元。每趟排序执行前,加入以下代码:
int mid = low + (high - low) / 2;//preventing overflow if (array[low] < array[mid]) { swap(&array[low], &array[mid]); } if (array[mid] < array[high] { swap(&array[mid], &array[high]); } if (array[high] < array[low]) { swap(&array[high], &array[low]); } int key = array[low];//pivot
五、随意位置的主元
有两种方法:
1、将主元交换至数组头或者尾,再用算法导论版本或者Hoare版本的快速排序
2、代码如下:
void QuickSort(int* array, int low, int high) { if (low >= high) return; int i = low; int j = high; int key = array[low + (high - low) / 2]; // partition while (i <= j) { while (array[j] > key) j--; while (array[i] < key) i++; if (i <= j) { swap(&array[i], &array[j]); i++; j--; } } // recursion QuickSort(array, low, j); QuickSort(array, i, high); }
六、链表排序
递归:
Node* QuickSort(Node* head, Node* tail) {//this algorithm separate the the linkedlist into two parts based on quicksort //one is less than the pivot, while the other part is no less than the pivot if (head->next == tail || head->next->next == tail) return head; Node* pivot = head->next; Node* current = pivot->next; Node* less_than_pivot = head; Node* more_than_pivot = pivot; while (current != tail) { if (current->data < pivot->data) { less_than_pivot->next = current; less_than_pivot = current; } else { more_than_pivot->next = current; more_than_pivot = current; } current = current->next; } less_than_pivot->next = pivot; more_than_pivot->next = tail; QuickSort(head, pivot); QuickSort(pivot, tail); return head; }
非递归:
链表的快排非递归实现与数组的相差不多,也是利用栈来完成的,这里就不给出了。