排序算法
最近看了《大话数据结构》简单的把上面的算法总结一下:
简单算法:冒泡排序,简单选择排序,直接插入排序
改进算法:希尔排序,堆排序,归并排序,快速排序
插入排序:直接插入排序和希尔排序
交换排序:冒泡排序和快速排序
选择排序:简单选择排序和堆排序
冒泡排序:
思想:两两比较相邻记录的关键字,大的放后面。这样,每一轮下来,最大的被放在了最后。
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; } }

浙公网安备 33010602011771号