排序算法
最近看了《大话数据结构》简单的把上面的算法总结一下:
简单算法:冒泡排序,简单选择排序,直接插入排序
改进算法:希尔排序,堆排序,归并排序,快速排序
插入排序:直接插入排序和希尔排序
交换排序:冒泡排序和快速排序
选择排序:简单选择排序和堆排序
冒泡排序:
思想:两两比较相邻记录的关键字,大的放后面。这样,每一轮下来,最大的被放在了最后。
Java代码: public int[] sort(int[] a){ int temp = 0; for (int i = a.length - 1; i > 0; --i){ for (int j = 0; j < i; ++j){ if (a[j + 1] < a[j]){ temp = a[j]; a[j] = a[j + 1]; a[j + 1] = temp; } } } return a; }
简单选择排序:
简单选择排序性能上略优于冒泡排序:
简单选择排序是从第一个元素开始(i=0),依次与其后的元素进行比较,找出最小元素的下标(j),放在下标是i的位置。
Java代码为: public int[] selectsort(int[] array){ int min,temp;//最小数标记 int len = array.Length; for(int i=0; i<len; i++){ min=i; for(int j=i+1; j<len; j++){ if(array[min]>array[j]){ min=j; } } tmp=array[i]; array[i]=array[min]; array[min]=tmp; } return array; }
直接插入排序:
直接插入排序是将一个记录插入到已经排好序的有序表中。
第一个元素不管了,相当于直接放进去的,然后后面的元素依次与前面的元素进行比较。所以下标是从1开始的。
Java代码如下: private int[] insertSort(int[] arr){ if(arr == null || arr.length < 2){ return arr; } for(int i=1;i<arr.length;i++){ for(int j=i;j>0;j--){ if(arr[j]<arr[j-1]){ int temp=arr[j]; arr[j]=arr[j-1]; arr[j-1]=temp; }else{ //接下来是无用功 break; } } } return arr; }
希尔排序:
希尔排序是插入排序的一种
一般的初次取序列的一半为增量,以后每次减半,直到增量为1。
public int[] ShellSort(int[] a){ int len=a.length; while(true){ len=len/2; for(int x=0;x<len;x++){ for(int i=x+len;i<a.length;i=i+len){ int temp=a[i]; int j; for(j=i-len;j>=0 && a[j]>temp;j=j-len){ //此for循环实现排序,差不多相当于插入排序 //与其前元素比较,当前元素比比较的元素小,其它元素后移 //给当前元素让位置 a[j+len]=a[j]; } //此元素比它前面的元素大,把空出来的位置放此元素 a[j+len]=temp; } } if(len==1){ break; } } return a; }
快速排序:
关键字靠近谁,保护谁(原理自己百度一下吧,死记),每一轮选出一个关键字,两个指针low和high,先选数组中的第一个元素作为关键字
int quicksort(vector<int> &v, int left, int right){ if(left < right){//两个指针没有重合 int key = v[left];//设置关键字 int low = left; int high = right; while(low < high){ while(low < high && v[high] > key){ high--; } v[low] = v[high]; while(low < high && v[low] < key){ low++; } v[high] = v[low]; } v[low] = key;//关键字被移动到low所在处 quicksort(v,left,low-1);//左边快速排序 quicksort(v,low+1,right);//右边快速排序 } }
堆排序:
大顶堆:从最后一个非叶子节点开始,往前逐步调整,让每个双亲都大于子女,至到根结点为止。每次找到最大结点后放置最后一个位置。然后再调整出最大的放在倒数第二个位置。
创建堆的时候就一个一个元素向二叉树上添加,不用管排序情况。序列表中前一半的程序就是此二叉树的非叶子节点。
//堆调整 void adjust(int arr[], int n, int length){ int max,b; while(n * 2 +1 <= length){//说明存在左节点 max = n * 2 + 1; if(n * 2 + 2 <= length){//说明存在右节点 if(arr[max] < arr[n * 2 + 2]){ max = n * 2 + 2; //跟新最小的值 } } if(arr[n] > =arr[max]){ break;//顺序正确,不需要再调整 }else{ b = arr[n]; arr[n] = arr[max]; arr[max] = b; n = max; } } } //堆排序 void stack(int arr[], int length){ int i,k,m = 0; for(i = length/2-1; i >=0; i--){ adjust(arr,i,length-1); } //调整堆 for(i = length-1 ;i >= 0; i--){ //调整后把最后一个和第一个交换,每次调整少一个元素,依次向前 k = arr[i]; arr[i] = arr[0]; arr[0] = k; adjust(arr,0,i-1); } }
归并排序:
可以把一个长度为n 的无序序列,等分后各部分再次等分,直到单个元素,看成是有序的,两两合并排序,再两两合并,…,如此重复,直到最后得到一个长度为 n 的有序序列。如下图所示:
sort(nums, 0, nums.length-1); public static int[] sort(int[] nums, int low, int high) { int mid = (low + high) / 2; if (low < high) { sort(nums, low, mid); sort(nums, mid + 1, high); merge(nums, low, mid, high); } return nums; } public static void merge(int[] nums, int low, int mid, int high) { int[] temp = new int[high - low + 1]; int i = low;// 左指针 int j = mid + 1;// 右指针 int k = 0; // 把较小的数先移到新数组中 while (i <= mid && j <= high) { if (nums[i] < nums[j]) { temp[k++] = nums[i++]; } else { temp[k++] = nums[j++]; } } // 把左边剩余的数移入数组 while (i <= mid) temp[k++] = nums[i++]; // 把右边边剩余的数移入数组 while (j <= high) temp[k++] = nums[j++]; // 把新数组中的数覆盖nums数组 for (int k2 = 0; k2 < temp.length; k2++) { nums[k2 + low] = temp[k2]; } }
各种常用排序算法 |
||||||||
类别 |
排序方法 |
时间复杂度 |
空间复杂度 |
稳定性 |
复杂性 |
特点 |
||
最好 |
平均 |
最坏 |
辅助存储 |
|
简单 |
|
||
插入 排序 |
直接插入 |
O(N) |
O(N2) |
O(N2) |
O(1) |
稳定 |
简单 |
|
希尔排序 |
O(N) |
O(N1.3) |
O(N2) |
O(1) |
不稳定 |
复杂 |
|
|
选择 排序 |
直接选择 |
O(N) |
O(N2) |
O(N2) |
O(1) |
不稳定 |
|
|
堆排序 |
O(N*log2N) |
O(N*log2N) |
O(N*log2N) |
O(1) |
不稳定 |
复杂 |
|
|
交换 排序 |
冒泡排序 |
O(N) |
O(N2) |
O(N2) |
O(1) |
稳定 |
简单 |
1、冒泡排序是一种用时间换空间的排序方法,n小时好 |
快速排序 |
O(N*log2N) |
O(N*log2N) |
O(N2) |
O(log2n)~O(n) |
不稳定 |
复杂 |
1、n大时好,快速排序比较占用内存,内存随n的增大而增大,但却是效率高不稳定的排序算法。 |
|
归并排序 |
O(N*log2N) |
O(N*log2N) |
O(N*log2N) |
O(n) |
稳定 |
复杂 |
1、n大时好,归并比较占用内存,内存随n的增大而增大,但却是效率高且稳定的排序算法。 |
|
基数排序 |
O(d(r+n)) |
O(d(r+n)) |
O(d(r+n)) |
O(rd+n) |
稳定 |
复杂 |
|
|
注:r代表关键字基数,d代表长度,n代表关键字个数 |
字符串匹配算法
/** * 暴力破解法 * * @param ss 主串 * @param ps 模式串 * @return 如果找到,返回在主串中第一个字符出现的下标,否则为-1 */ public int violentMatch(String ss, String ps) { char[] s = ss.toCharArray(); char[] p = ps.toCharArray(); int i = 0; // 主串的位置 int j = 0; // 模式串的位置 while (i < s.length && j < p.length) { if (s[i] == p[j]) { //①如果当前字符匹配成功(即s[i]==p[j]),则i++,j++ i++; j++; } else { //②如果失败(即s[i]!=p[j]),令i=i-j+1,j=0 i = i - j + 1; j = 0; } } if (j == p.length) { return i - j; } else { return -1; } }