排序算法

最近看了《大话数据结构》简单的把上面的算法总结一下:

简单算法:冒泡排序,简单选择排序,直接插入排序
改进算法:希尔排序,堆排序,归并排序,快速排序
插入排序:直接插入排序和希尔排序
交换排序:冒泡排序和快速排序
选择排序:简单选择排序和堆排序

冒泡排序:

  思想:两两比较相邻记录的关键字,大的放后面。这样,每一轮下来,最大的被放在了最后。

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小时好
2、最坏情况是把顺序的排列变成逆序,或者把逆序的数列变成顺序,最差时间复杂度O(N^2)只是表示其操作次数的数量级
3、最好的情况是数据本来就有序,复杂度为O(n)

快速排序

O(N*log2N)

O(N*log2N) 

O(N2)

O(log2n)~O(n) 

不稳定

复杂

1、n大时好,快速排序比较占用内存,内存随n的增大而增大,但却是效率高不稳定的排序算法。
2、划分之后一边是一个,一边是n-1个,
这种极端情况的时间复杂度就是O(N^2)
3、最好的情况是每次都能均匀的划分序列,O(N*log2N)

归并排序

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;
        }
    }

 

posted @ 2017-07-14 13:08  岁月无言成追忆  阅读(299)  评论(0编辑  收藏  举报