算法相关概念

  • 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
  • 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
  • 时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
  • 空间复杂度:指算法在计算机内执行时所需存储空间的度量,也是数据规模n的函数。

插入排序

简单插入排序

  • 从第一个元素开始,该元素可以认为已经被排序;
  • 取出下一个元素,在已经排序的元素序列中从后向前扫描;
  • 如果该元素(已排序)大于新元素,将该元素移到下一位置;
  • 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
  • 将新元素插入到该位置;

时间复杂度:平均复杂度为O(n^2),最好情况O(n),最坏情况O(n^2)
空间复杂度:O(n)
稳点性:稳定

  1. public class InsertSort {
  2. public static void insertSort(int[] data){
  3. int len = data.length-1;
  4. int i,j;
  5. //从1开始,第0位只有一个数,认为是有序的
  6. for(i=1; i<=len; i++){
  7. int compare = data[i];
  8. for(j=i-1; j>=0; j--){
  9. if(compare > data[j]){
  10. break;
  11. }else {
  12. data[j+1] = data[j];
  13. }
  14. }
  15. //跳出循环后j自减了一次,所以要加1
  16. data[j+1] = compare;
  17. }
  18. }
  19. public static void main(String[] args) {
  20. int[] data = new int[]{8,6,12,45,78,2,36,54};
  21. insertSort(data);
  22. System.out.println(Arrays.toString(data));
  23. }
  24. }

849589-20171015225645277-1151100000.gif

希尔排序

在插入排序的基础上,先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:

  • 选择一个增量序列t1,t2,…,tk
  • 按增量序列个数k,对序列进行k 趟排序;
  • 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

时间复杂度:平均复杂度为O(n^2),最好情况O(n),最坏情况O(n^2)
空间复杂度:O(n)
稳点性:不稳定

  1. public class ShellSort {
  2. public static void shellSort(int[] data){
  3. int len = data.length-1;
  4. int i,j;
  5. for(int n=len/2; n>=1; n=n/2){
  6. //与插入排序相比,i=1换成i=n
  7. for(i=n; i<=len; i++){
  8. int compare = data[i];
  9. //j--换成 j-=n
  10. for(j=i-n; j>=0; j=j-n){
  11. if(compare > data[j]){
  12. break;
  13. }else {
  14. //j+1换成j+n
  15. data[j+n] = data[j];
  16. }
  17. }
  18. //j+1换成j+n
  19. data[j+n] = compare;
  20. }
  21. }
  22. }
  23. public static void main(String[] args) {
  24. int[] data = {9,6,4,24,35,7,31,26};
  25. shellSort(data);
  26. System.out.println(Arrays.toString(data));
  27. }
  28. }

选择排序

简单选择排序

选择排序是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小元素,然后与未排序序列的起始元素交换位置。剩余未排序序列重复以上操作

  • 初始状态:数组data[len]未排序,有序序列为空,无序序列从数组第一个元素到最后一个元素
  • 经过第i轮排序(i=1,2,3…n-1),当前有序区和无序区分别为data(0..i-1)和data(i…len)从data(i…len)中选出最小的元素与data[i]交换位置
  • n-1趟结束,数组排序完成

时间复杂度:平均复杂度为O(n^2),最好情况O(n^2),最坏情况O(n^2)
空间复杂度:O(n)
稳点性:不稳定

  1. public class SelectSort {
  2. public static void selectSort(int[] data){
  3. int len = data.length-1;
  4. int i,j;
  5. for(i=0; i<=len; i++){
  6. int min = i;
  7. for(j=i+1; j<=len; j++){
  8. if(data[j] < data[min]){
  9. //更新最小下标
  10. min = j;
  11. }
  12. }
  13. //元素互换
  14. int temp = data[i];
  15. data[i] = data[min];
  16. data[min] = temp;
  17. }
  18. }
  19. public static void main(String[] args) {
  20. int[] data = new int[]{8,6,12,45,78,2,36,54};
  21. selectSort(data);
  22. System.out.println(Arrays.toString(data));
  23. }
  24. }

849589-20171015224719590-1433219824.gif

冒泡排序

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较相邻的两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

  • 比较相邻的元素,如果第一个比第二个大,就交换它们两个;
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
  • 针对所有的元素重复以上的步骤,除了最后一个

时间复杂度:平均复杂度为O(n^2),最好情况O(n^2),最坏情况O(n^2)
空间复杂度:O(n)
稳点性:不稳定

  1. public class BubbleSort {
  2. public static void bubbleSort(int[] data){
  3. int len = data.length-1;
  4. int i,j;
  5. //记录尾部已排好序的元素个数
  6. for(i=0; i<len; i++){
  7. for(j=0; j<len-i; j++){
  8. if(data[j] > data[j+1]){
  9. int temp = data[j];
  10. data[j] = data[j+1];
  11. data[j+1] = temp;
  12. }
  13. }
  14. }
  15. }
  16. public static void main(String[] args) {
  17. int[] data = new int[]{8,6,12,45,78,2,36,54};
  18. bubbleSort(data);
  19. System.out.println(Arrays.toString(data));
  20. }
  21. }

849589-20171015223238449-2146169197.gif

快速排序

快速排序的基本思想:选取一个基准数,通过不断交换,以基准数为中心分成两个序列,基准数左边的序列均比基准数小,基准数右边的序列均比基准数大。对这两个序列又选取一个基准数继续上述操作

  • 从数列中挑出一个元素作为基准数
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边),在这个分区退出之后,该基准就处于数列的中间位置
  • 递归地把小于基准值元素的子序列和大于基准值元素的子序列进行上述操作

时间复杂度:平均复杂度为O(nlogn),最好情况O(nlogn),最坏情况O(n^2)
空间复杂度:O(n)
稳点性:不稳定

  1. public class QuickSort {
  2. public static void quickSort(int[] data, int left, int right){
  3. //区第一个数为基准数
  4. int base = data[left];
  5. int ll = left;
  6. int rr = right;
  7. //终止条件 ll=rr
  8. while(ll < rr){
  9. //找到第一个比base小的数,跳出循环
  10. while (ll<rr && data[rr] >= base){
  11. rr--;
  12. }
  13. //与基准数交换位置
  14. if(ll<rr){
  15. int temp = data[rr];
  16. data[rr] = data[ll];
  17. data[ll] = temp;
  18. ll++;
  19. }
  20. while(ll<rr && data[ll] <= base){
  21. ll++;
  22. }
  23. if(ll < rr){
  24. int temp = data[ll];
  25. data[ll] = data[rr];
  26. data[rr] = temp;
  27. rr--;
  28. }
  29. //开始尾递归
  30. if(left < ll){
  31. quickSort(data, left, ll-1);
  32. }
  33. if(right > rr){
  34. quickSort(data, rr+1, right);
  35. }
  36. }
  37. }
  38. public static void main(String[] args) {
  39. int[] data = new int[]{8,6,12,45,78,2,36,54};
  40. quickSort(data, 0, data.length-1);
  41. System.out.println(Arrays.toString(data));
  42. }
  43. }

849589-20171015230936371-1413523412.gif

归并排序

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

  • 把长度为n的输入序列分成两个长度为n/2的子序列;
  • 对这两个子序列分别采用归并排序;
  • 将两个排序好的子序列合并成一个最终的排序序列。

时间复杂度:平均复杂度为O(nlogn),最好情况O(nlogn),最坏情况O(nlogn)
空间复杂度:O(2n)
稳点性:稳定

  1. public class MegerSort {
  2. public static void main(String[] args) {
  3. int[] data = {9,6,4,24,35,7,31,26};
  4. int n = data.length;
  5. megerSort(data,0,n-1);
  6. System.out.println(Arrays.toString(data));
  7. }
  8. public static void megerSort(int[] data, int left, int right){
  9. if(left < right){
  10. int mid = (left + right) / 2;
  11. megerSort(data, left, mid);
  12. megerSort(data, mid+1, right);
  13. meger(data, left, mid, right);
  14. }
  15. }
  16. public static void meger(int[] data, int left, int mid, int right){
  17. int[] temp = new int[data.length];
  18. int point1 = left;
  19. int point2 = mid+1;
  20. int loc = left;
  21. while (point1 <= mid && point2 <= right){
  22. if(data[point1] < data[point2]){
  23. temp[loc] = data[point1];
  24. point1 ++;
  25. loc ++;
  26. }else {
  27. temp[loc] = data[point2];
  28. point2 ++;
  29. loc ++;
  30. }
  31. }
  32. while (point1 <= mid){
  33. temp[loc++] = data[point1++];
  34. }
  35. while (point2 <= right){
  36. temp[loc++] = data[point2++];
  37. }
  38. for(int i = left; i<=right; i++){
  39. data[i] = temp[i];
  40. }
  41. }
  42. }

849589-20171015230557043-37375010.gif