赞
踩
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为: 任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
实现思路:取left为tmp则从right开始,取right为tmp则从left开始,这里我们取left为tmp, right先走,遇到比tmp小的值停下,然后left开始走遇到比tmp大的值停止,然后left和right所指向的数据交换位置,然后继续重复,直到right和left相遇,相遇位置的数据和tmp交换此时以tmp为中心,数组被分成了两个部分, 然后将两个部分看成新数组,进行递归排序,当数组只有一个值的时候排序结束
- //1.quare法
- int partsort1(int* a, int begin, int end)
- {
- int left = begin, right = end;
- int tmp = left;
- while (left < right)
- {
- while (left < right && a[right] >= a[tmp])
- {
- right--;
- }
- while (left < right && a[left] <= a[tmp])
- {
- left++;
- }
-
- Swap(&a[left], &a[right]);
- }
- Swap(&a[tmp], &a[right]);
- tmp = left;
- return tmp;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
基本思路:挖坑法顾名思义就是一个挖坑填坑的过程,tmp在那边,坑就从那边开始, 如图left为tmp,则坑就在left,然后right开始找小,找到后将其值赋给坑,此时right变新坑,然后left找大,找到后将其值赋给坑,然后left变新坑,当left和right相遇时,将tmp放到坑里,单次循环结束,然后数组以tmp为中心被分成两个部分, 每个部分看成一个新数组,进行递归排序,当数组只剩下一个值的时候,排序完成。
- //2.挖坑法
- int partsort2(int* a, int begin, int end)
- {
- int left = begin, right = end;
- int tmp = a[left];
- int pit = left;
- while (left < right)
- {
- while (left < right && a[right] >= tmp)
- {
- right--;
- }
- a[pit] = a[right];
- pit = right;
- while (left < right && a[left] <= tmp)
- {
- left++;
- }
- a[pit] = a[left];
- pit = left;
- }
- a[pit] = tmp;
- return pit;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
基本思想: 设立前后指针prev,cur,取左边为tmp,然后cur找小,找到大时cur跳过,当找到小的时候,此时prev+1要么和cur重合,要么prev+1指向的值大于tmp,所以prev+1和cur指向的值交换,当cur越界的时候,prev和tmp交换,此时以tmp为中心,分成两个部分, 每个部分看成一个新的数组进行递归排序。
- //3.双指针法
- int partsort3(int* a, int begin, int end)
- {
- int prev = begin;
- int cur = begin + 1;
- int tmp = begin;
- while (cur<=end)
- {
- if (a[cur] < a[tmp])
- {
- prev++;
- Swap(&a[prev], &a[cur]);
- }
- cur++;
- }
- Swap(&a[tmp], &a[prev]);
- tmp = prev;
- return tmp;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
基本思路:快排的递归实现主要是着力于 快排基本框架和二叉树的前序遍历类似,所以用利用二叉树的前序遍历递归写出即可。
代码后续会贴出。
基本思路:非递归快排我们用 栈的的后进先出来实现(又是c语言库里没有栈,所以需要自己写,不太了解的老铁可以看我之前的文章,有写如何实现栈), 因为快排每次排序都将数组分成两个部分,所以我们先将最开始的数组的左右下标压入栈中,然后在出栈,对其进行排序,然后把新的到的两个数组各自的左右下标压入栈中,当栈为空的时候,排序完成。
1.tmp是最大或者最小的一些特殊情况下,时间复杂度为0(N^2)
2.当数比较多的时候,递归所需要开辟的栈过多,容易造成栈溢出
1.三数取中法
三数取中法就是,取left,right以及数组中间的值,比较三者的大小,取中间值作为tmp,这样就避免了tmp是最大或者最小值的情况。
- //三数取中法优化
- int GetMidIndex(int* a, int begin, int end)
- {
- int min = (begin + end) / 2+1;
- if (a[begin] < a[min])
- {
- if (a[min] < a[end])
- {
- return min;
- }
- else if (a[begin] > a[end])
- {
- return begin;
- }
- else
- {
- return end;
- }
- }
- else
- {
- if (a[begin] < a[end])
- {
- return begin;
- }
- else if (a[min] > a[end])
- {
- return min;
- }
- else
- {
- return end;
- }
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
2.小区间优化法
当数组长度较小时,也就是递归展开的最后几层,所需要的栈占总需求的大部分,此时如果用插入排序就可以有效避免栈溢出的现象。
代码后续贴出。
1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(logN)
4. 稳定性:不稳定,如下图所示
- //1.quare法
- int partsort1(int* a, int begin, int end)
- {
- int left = begin, right = end;
- int tmp = left;
- while (left < right)
- {
- while (left < right && a[right] >= a[tmp])
- {
- right--;
- }
- while (left < right && a[left] <= a[tmp])
- {
- left++;
- }
-
- Swap(&a[left], &a[right]);
- }
- Swap(&a[tmp], &a[right]);
- tmp = left;
- return tmp;
- }
- //2.挖坑法
- int partsort2(int* a, int begin, int end)
- {
- int left = begin, right = end;
- int tmp = a[left];
- int pit = left;
- while (left < right)
- {
- while (left < right && a[right] >= tmp)
- {
- right--;
- }
- a[pit] = a[right];
- pit = right;
- while (left < right && a[left] <= tmp)
- {
- left++;
- }
- a[pit] = a[left];
- pit = left;
- }
- a[pit] = tmp;
- return pit;
- }
- //3.双指针法
- int partsort3(int* a, int begin, int end)
- {
- int prev = begin;
- int cur = begin + 1;
- int tmp = begin;
- while (cur<=end)
- {
- if (a[cur] < a[tmp])
- {
- prev++;
- Swap(&a[prev], &a[cur]);
- }
- cur++;
- }
- Swap(&a[tmp], &a[prev]);
- tmp = prev;
- return tmp;
- }
- //三数取中法优化
- int GetMidIndex(int* a, int begin, int end)
- {
- int min = (begin + end) / 2+1;
- if (a[begin] < a[min])
- {
- if (a[min] < a[end])
- {
- return min;
- }
- else if (a[begin] > a[end])
- {
- return begin;
- }
- else
- {
- return end;
- }
- }
- else
- {
- if (a[begin] < a[end])
- {
- return begin;
- }
- else if (a[min] > a[end])
- {
- return min;
- }
- else
- {
- return end;
- }
- }
- }
- //快排
- void QuickSort(int* a, int begin, int end)
- {
- if (begin >= end)
- {
- return;
- }
- int tmp = GetMidIndex(a, begin, end);
- //小区间优化
- if (end - begin < 15)
- {
- InsertSort(a + begin, end - begin+1);
- }
- else
- {
- tmp = partsort2(a, begin, end);
- QuickSort(a, begin, tmp - 1);
- QuickSort(a, tmp + 1, end);
- }
-
- }
- //非递归快排
- void QuickSortNonR(int* a, int begin, int end)
- {
- Stack st;
- StackInit(&st);
- StackPush(&st, begin);
- StackPush(&st, end);
- while (StackEmpty(&st))
- {
- //后进先出,右边后进的所以先取右边
- int right = StackTop(&st);
- StackPop(&st);
- int left = StackTop(&st);
- StackPop(&st);
- int tmp = partsort1(a, left, right);
- //先排左边,就要先入右边
- if (tmp + 1 < right)
- {
- StackPush(&st, tmp + 1);
- StackPush(&st, right);
- }
- if (left < tmp - 1)
- {
- StackPush(&st, left);
- StackPush(&st, tmp-1);
- }
- }
- StackDestroy(&st);
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:
基本思路:因为归并排序的展开图和二叉树类似,而且归并排序的核心思想是先让子序段有序,在合并,所以可以用 二叉树的后序遍历的思想来实现
单次排序的思路;单次排序类似于将 两个有序数组合并成一个有序数组的过程,先创建一个长度和原数组长度相等的数组tmp,然后将两个有序的子序数组取小尾插进tmp里,全部插入后,再将tmp的数据memcpy进原数组
- //归并排序单次排序
- void _MergeSort(int* a, int begin, int end, int* tmp)
- {
- if (begin >= end)
- {
- return;
- }
- int mid = (begin + end) / 2;
- //分解
- _MergeSort(a, begin, mid, tmp);
- _MergeSort(a, mid+1, end, tmp);
- //合并
- int begin1 = begin,begin2=mid+1;
- int i = begin;
- while (begin1 <= mid && begin2 <= end)
- {
- if (a[begin1] <= a[begin2])
- {
- tmp[i++] = a[begin1++];
- }
- else
- {
- tmp[i++] = a[begin2++];
- }
- }
- while (begin1 <= mid)
- {
- tmp[i++] = a[begin1++];
- }
- while (begin2 <= end)
- {
- tmp[i++] = a[begin2++];
-
- }
- memcpy(a+begin, tmp+begin, sizeof(int)*(end - begin + 1));
-
- }
- //归并排序递归
- void MergeSort(int* a, int n)
- {
- int* tmp = (int*)malloc(sizeof(int) * n);
- if (tmp == NULL)
- {
- perror("malloc");
- exit(-1);
- }
- _MergeSort(a, 0, n - 1, tmp);
- free(tmp);
- tmp = NULL;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
基本思路:设置一个参数rangeN来模拟实现归并排序,当rangeN=1时模拟的是递归的最后一层的实现,为2则是倒数第二层的实现,每次rangeN*2,当rangeN大于数组长度时,排序完成。
- //归并排序非递归
- void MergeSortNonR(int* a, int n)
- {
- int* tmp = (int*)malloc(sizeof(int) * n);
- if (tmp == NULL)
- {
- perror("malloc");
- exit(-1);
- }
- int rangeN = 1;
- while (rangeN < n)
- {
- for (int i = 0; i < n; i += rangeN * 2)
- {
- int begin1 = i, end1 = i + rangeN - 1;
- int begin2 = end1 + 1, end2 = begin2 + rangeN - 1;
- int j = i;
- //判断越界情况
- //end1,begin2,end2都越界
- if (end1 >= n)
- {
- break;
- }
- //begin2,end2越界
- else if (begin2 >= n)
- {
- break;
- }
- //end2越界
- else if (end2 >= n)
- {
- end2 = n - 1;
- }
- while (begin1 <= end1 && begin2 <= end2)
- {
- if (a[begin1] <= a[begin2])
- {
- tmp[j++] = a[begin1++];
- }
- else
- {
- tmp[j++] = a[begin2++];
- }
- }
- while (begin1 <= end1)
- {
- tmp[j++] = a[begin1++];
- }
- while (begin2 <= end2)
- {
- tmp[j++] = a[begin2++];
-
- }
- memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
- }
- rangeN *= 2;
- }
-
-
-
- free(tmp);
- tmp = NULL;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
注意:写代码时需要注意一下数组越界的情况。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。