当前位置:   article > 正文

数据结构之常见排序算法

数据结构之常见排序算法

目录

一、排序中的概念

二、常见排序算法--升序

1、插入排序

(1)直接插入排序

(2)希尔排序

2、选择排序

(1)单指针选择排序

(2)双指针选择排序

(3)堆排序

3、交换排序

(1)冒泡排序

(2)双指针快速排序

(3)挖洞快速排序

(4)前后指针快速排序

(5)优化版快速排序

(6)非递归快速排序

4、归并排序

(1)归并排序

(2)归并排序非递归

5、计数排序

6、桶排序

7、基数排序

一、排序中的概念

1、稳定性:在待排序的记录中,存在多个相同的关键字记录,若在排序前和排序后这些相同关键字的相对次序保持不变,则这种排序算法是稳定的,否则称为不稳定的。

二、常见排序算法--升序

1、插入排序
(1)直接插入排序

①思想:将要插入的数据和前面比较,要是前面的小,结束比较,要是前面的大,元素后移,再次比较,直到遇到较小的,元素插入。

②代码实现:

  1. public static void insertSort(int[] a){
  2. int size=a.length;
  3. for(int i=1;i<size;i++){
  4. int j=i-1;
  5. int temp=a[i]; //后面会被覆盖
  6. for(;j>=0;j--){
  7. if(a[j]>temp){
  8. a[j+1]=a[j];
  9. }else {
  10. break;
  11. }
  12. }
  13. a[j+1]=temp;
  14. }
  15. }

③特性:

元素越接近有序,时间效率越高;

时间复杂度:O(n^2);空间复杂度:O(1);稳定性:稳定。

(2)希尔排序

①思想:对元素进行分组,如有10个元素,先分成5组(元素间隔为5),对着5组进行插入排序;再分成2组(元素间隔为2),对这2组进行插入排序;最后分成1组(元素间隔为1),对着1组进行插入排序,此时插入排序时元素已经接近有序了。

②代码实现:

  1. public static void shellSort(int[] a){
  2. int size=a.length;
  3. while(size/2>=1){
  4. size/=2;
  5. shell(a,size);
  6. }
  7. }
  8. public static void shell(int[] a,int group){
  9. int size=a.length;
  10. for(int i=1;i<size;i++){
  11. int temp=a[i];
  12. int j=i-group;
  13. for(;j>=0;j-=group){
  14. if(a[j]>temp){
  15. a[j+group]=a[j];
  16. }else{
  17. break;
  18. }
  19. }
  20. a[j+group]=temp;
  21. }
  22. }

③特性:

是直接插入排序的优化。

时间复杂度:O(n^1.25)到O(1.6n^1.25);空间复杂度:O(1);稳定性:不稳定。

2、选择排序
(1)单指针选择排序

①思想:

从第一个数开始,每次找到待排序数的最小值,与第一个数交换位置;然后开始第二个数,每次找到待排序数的最小值,与第二个数交换位置......直到最后一个数结束。

②代码实现:

  1. public static void selectSort(int[] a){
  2. int size=a.length;
  3. for(int i=0;i<size-1;i++){
  4. int min=i;
  5. for(int j=i+1;j<size;j++){
  6. if(a[j]<a[min]){
  7. min=j;
  8. }
  9. }
  10. if(i!=min){
  11. swap(a,i,min);
  12. }
  13. }
  14. }
  15. public static void swap(int[] a,int m,int n){
  16. int temp=a[m];
  17. a[m]=a[n];
  18. a[n]=temp;
  19. }

③特性:

时间复杂度:O(n^2);空间复杂度:O(1);稳定性:不稳定。

(2)双指针选择排序

①思想:

一个指针指向要排序数的最小值,一个指针指向要排序数的最大值,最小值放左边,最大值放右边,往中间遍历,但需注意:最大值在要排序数第一个的情况。

②代码实现:

  1. public static void selectSort2(int[] a){
  2. int size=a.length;
  3. int left=0;
  4. int right=size-1;
  5. while(left<right){
  6. int max=right;
  7. int min=left;
  8. for(int i=left;i<=right;i++){
  9. if(a[i]>a[max]){
  10. max=i;
  11. }else if(a[i]<a[min]){
  12. min=i;
  13. }
  14. }
  15. if(max==left){
  16. max=min;
  17. }
  18. if(min!=left){
  19. swap(a,min,left);
  20. }
  21. if(max!=right){
  22. swap(a,max,right);
  23. }
  24. left++;
  25. right--;
  26. }
  27. }
  28. public static void swap(int[] a,int m,int n){
  29. int temp=a[m];
  30. a[m]=a[n];
  31. a[n]=temp;
  32. }
(3)堆排序

①思想:

若是升序的话,建立大根堆,每次将堆顶元素与最后一个元素交换,前n-1个元素,以第一个父结点进行向下调整......直到交换结束。

若是降序的话,建立小根堆,每次将堆顶元素与最后一个元素交换,前n-1个元素,以第一个父结点进行向下调整......直到交换结束。

②代码实现:

  1. public static void buildPile(int[] a){
  2. int size=a.length;
  3. int parent=(size-2)/2;
  4. for(int i=parent;i>=0;i--){
  5. buildChildPile(a,i,size);
  6. }
  7. }
  8. public static void buildChildPile(int[] a,int parent,int size){
  9. int child=2*parent+1;
  10. while(child<size){
  11. if(child+1<size&&a[child]<a[child+1]){
  12. child=child+1;
  13. }
  14. if(a[child]>a[parent]){
  15. swap(a,child,parent);
  16. }
  17. parent=child;
  18. child=2*parent+1;
  19. }
  20. }
  21. public static void pileSort(int[] a){
  22. int size=a.length-1;
  23. while(size!=0){
  24. swap(a,0,size);
  25. buildChildPile(a,0,size-1);
  26. size--;
  27. }
  28. }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(1);稳定性:不稳定。

3、交换排序
(1)冒泡排序

①思想:

将大的往左边放,每次遇到一个数,比后面数大,就往后交换。

②代码实现:

  1. public static void bubbleSort(int[] a){
  2. int size=a.length;
  3. boolean flag=true;
  4. for(int i=0;i<size-1;i++){
  5. for(int j=1;j<size-i;j++){
  6. if(a[j-1]>a[j]){
  7. swap(a,j-1,j);
  8. flag=false;
  9. }
  10. }
  11. if(flag){
  12. break;
  13. }
  14. }
  15. }

③特性:

时间复杂度:O(n^2);空间复制度:O(1);稳定性:稳定。

(2)双指针快速排序

①思想:找到一个基准值,默认为left指针,right往左移,left往右移,如果right小于基准,left大于基准,两者交换,循环进行到left<right不满足,基准值要和最后一个left交换,接着划分区间进行判断,直到区间里只有一个元素结束。

②代码实现:

  1. public static void quickSort(int[] a){
  2. quickChild(a,0,a.length-1);
  3. }
  4. public static void quickChild(int[] a,int left,int right){
  5. if(left>=right){ //一个元素
  6. return;
  7. }
  8. int index=index(a,left,right); //进行交换,并找到基准值的位置
  9. quickChild(a,left,index-1); //左区间接着判断
  10. quickChild(a,index+1,right); //右区间接着判断
  11. }
  12. public static int index(int[] a,int left,int right){
  13. int temp=a[left];
  14. int k=left;
  15. while (left<right){
  16. while(left<right&&a[right]>=temp){
  17. right--;
  18. }
  19. while(left<right&&a[left]<=temp){
  20. left++;
  21. }
  22. swap(a,left,right);
  23. }
  24. swap(a,k,left);
  25. return left; //left后走的 right一定是走到了小于等于temp的地方
  26. }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。

(3)挖洞快速排序

①思想:找到一个基准值,默认为left指针,right往左移,left往右移,如果right小于基准,right和left交换;如果left大于基准,left和right交换,循环进行到left<right不满足,基准值放在最后一个left,接着划分区间进行判断,直到区间里只有一个元素结束。

②代码实现:

  1. public static void quickSort2(int[] a){
  2. quickChild2(a,0,a.length-1);
  3. }
  4. public static void quickChild2(int[] a,int left,int right){
  5. if(left>=right){
  6. return;
  7. }
  8. int index=index2(a,left,right);
  9. quickChild2(a,left,index-1);
  10. quickChild2(a,index+1,right);
  11. }
  12. public static int index2(int[] a,int left,int right){
  13. int temp=a[left];
  14. while(left<right){
  15. while(left<right&&a[right]>=temp){
  16. right--;
  17. }
  18. swap(a,left,right);
  19. while(left<right&&a[left]<=temp){
  20. left++;
  21. }
  22. swap(a,left,right);
  23. }
  24. a[left]=temp;
  25. return left;
  26. }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。

(4)前后指针快速排序

①思想:前指针pre为left,后指针cur为前指针+1,当cur<=right时,若指针left的值大于指针cur的值,且++pre指针的值不等于cur,交换cur与pre的值,否则不交换;cur++;循环结束后交换left与pre的值。  ----琢磨

②代码实现:

  1. public static void quickSort3(int[] a){
  2. quickChild3(a,0,a.length-1);
  3. }
  4. public static void quickChild3(int[] a,int left,int right){
  5. if(left>=right){
  6. return;
  7. }
  8. int index=index3(a,left,right);
  9. quickChild3(a,left,index-1);
  10. quickChild3(a,index+1,right);
  11. }
  12. public static int index3(int[] a,int left,int right){
  13. int pre=left;
  14. int cur=pre+1;
  15. while(cur<=right){
  16. if(a[left]>a[cur]&&a[++pre]!=a[cur]){
  17. swap(a,pre,cur);
  18. }
  19. cur++;
  20. }
  21. swap(a,left,pre);
  22. return pre;
  23. }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。

(5)优化版快速排序

①思想:长度如果小于等于5采用直接插入排序;基准值不再默认为left的值,选择left、right、mid三者的中间值。

②代码实现:

  1. public static void quickSort4(int[] a){
  2. int size=a.length;
  3. if(size<=5){
  4. insertSort(a);
  5. }else{
  6. quickChild4(a,0,size-1);
  7. }
  8. }
  9. public static void quickChild4(int[] a,int left,int right){
  10. if(left>=right){
  11. return;
  12. }
  13. int mid=findMid(a,left,right);
  14. swap(a,mid,left); //改变基准值
  15. int index=index4(a,left,right);
  16. quickChild4(a,left,index-1);
  17. quickChild4(a,index+1,right);
  18. }
  19. public static int index4(int[] a,int left,int right){
  20. int temp=a[left];
  21. while(left<right){
  22. while(left<right&&a[right]>=temp){
  23. right--;
  24. }
  25. swap(a,left,right);
  26. while(left<right&&a[left]<=temp){
  27. left++;
  28. }
  29. swap(a,left,right);
  30. }
  31. a[left]=temp;
  32. return left;
  33. }
  34. public static int findMid(int[] a,int left,int right){
  35. int mid=(left+right)/2;
  36. if(a[left]>a[right]){
  37. if(a[mid]<a[right]){
  38. return right;
  39. }else if(a[mid]>a[left]){
  40. return left;
  41. }else {
  42. return mid;
  43. }
  44. }else{
  45. if(a[mid]<a[left]){
  46. return left;
  47. }else if(a[mid]>a[right]){
  48. return right;
  49. }else {
  50. return mid;
  51. }
  52. }
  53. }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。

(6)非递归快速排序

①思想:和双指针思想一样,只是此时将left和right的值放进了队列中,只要队列不为空,再拿出来进行交换并获取下标。

②代码实现:

  1. public static void quickSort5(int[] a){
  2. int right=a.length-1;
  3. int left=0;
  4. if(right-left>1){
  5. quickChild5(a,left,right);
  6. }
  7. }
  8. public static void quickChild5(int[] a,int left,int right){
  9. Queue<Integer> queue=new LinkedList<>();
  10. queue.offer(left);
  11. queue.offer(right);
  12. while (!queue.isEmpty()){
  13. int start=queue.poll();
  14. int end=queue.poll();
  15. int index=index(a,start,end);
  16. if(index-1>start){
  17. queue.offer(start);
  18. queue.offer(index-1);
  19. }
  20. if(right>index+1){
  21. queue.offer(index+1);
  22. queue.offer(right);
  23. }
  24. }
  25. }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。

4、归并排序
(1)归并排序

①思想:不断划分区间,直到区间内只有一个元素时结束,然后在区间内进行排序(类似于双指针中的滑动窗口,指针指的数谁小,数往前放,指针后移)合并。

②代码实现:

  1. public static void mergeSort(int[] a){
  2. mergeChild(a,0,a.length-1);
  3. }
  4. public static void mergeChild(int[] a,int left,int right){
  5. if(left>=right){
  6. return;
  7. }
  8. int mid=(left+right)/2;
  9. mergeChild(a,left,mid); //左边拆分
  10. mergeChild(a,mid+1,right); //右边拆分
  11. merge(a,left,mid,right); //合并
  12. }
  13. public static void merge(int[] a,int left,int mid,int right){
  14. int s1=left;
  15. int e1=mid;
  16. int s2=mid+1;
  17. int e2=right;
  18. int[] temp=new int[right-left+1];
  19. int k=0;
  20. while(s1<=e1&&s2<=e2){
  21. if(a[s1]<a[s2]){
  22. temp[k++]=a[s1++];
  23. }else {
  24. temp[k++]=a[s2++];
  25. }
  26. }
  27. if(s1>e1){
  28. while(s2<=e2){
  29. temp[k++]=a[s2++];
  30. }
  31. }else {
  32. while(s1<=e1){
  33. temp[k++]=a[s1++];
  34. }
  35. }
  36. for(int i=0;i<temp.length;i++){
  37. a[i+left]=temp[i]; //a是从left开始的 temp是从0开始的
  38. }
  39. }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(n);稳定性:稳定。

(2)归并排序非递归

①思想:每次直接求出left,right,mid,然后调用方法进行排序。left根据分组元素求出,mid=left+group-1,right=mid+group。

②代码实现:

  1. public static void mergeSort2(int[] a){
  2. int group=1; //区间内元素个数:group+1
  3. int size=a.length;
  4. while(group<size){
  5. for(int i=0;i<size;i+=group*2) { //left值
  6. int left=i;
  7. int mid=left+group-1;
  8. if(mid>size-1){ //区间里元素个数不够凑成一个group 可能出现mid越界
  9. mid=size-1;
  10. }
  11. int right=mid+group;
  12. if(right>size-1){
  13. right=size-1;
  14. }
  15. merge(a,left,mid,right); //进行合并
  16. }
  17. group*=2;
  18. }
  19. }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(n);稳定性:稳定。

5、计数排序

①思想:统计每个数据出现的次数,并对应下标按照顺序存储,按照顺序再放入排序数组。

②代码实现:

  1. public static void countSort(int[] a){
  2. int size=a.length;
  3. int min=a[0];
  4. int max=a[0];
  5. for(int i=0;i<size;i++){
  6. if(a[i]>max){
  7. max=a[i];
  8. }else if(a[i]<min){
  9. min=a[i];
  10. }
  11. }
  12. int[] count=new int[max-min+1];
  13. for(int i=0;i<size;i++){
  14. count[a[i]-min]++;
  15. }
  16. int j=0;
  17. for(int i=0;i<count.length;i++){
  18. while (count[i]>0){
  19. a[j++]=i+min;
  20. count[i]--;
  21. }
  22. }
  23. }

③特性:

时间复杂度:O(n);空间复杂度:O(范围);稳定性:稳定

6、桶排序

①思想:

7、基数排序

①思想:

个位数排序:

十位数排序:

百位数排序:

此时就已经有序了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/484620
推荐阅读
相关标签
  

闽ICP备14008679号