数据结构----常用排序算法总结(JAVA)

最近因为找实习,重新梳理了一遍数据结构中常用排序算法,仅供学习参考,如有不足之处,敬请指正。

目录


 

插入排序

  • 直接插入排序

基本思路:将数组看成一个有序数组,一个无序数组

public static void insertSort(int[] arr){
	//数组是空或者数组里面只有一个元素,没必要排序
	if (null == arr || arr.length < 2){
		return;
	}
	for(int i=1;i<n;i++){
		int temp = arr[i];
		int j=i;
		while(j-1>=0 && temp<arr[j-1]){
			arr[j]=arr[j-1];
			j--;
		}
		arr[j]=temp;
	}
}
  • 折半插入排序

public static void binaryInsertSort(int[] arr){
	for(int i=1;i<arr.length;i++){
		if(arr[i] < arr[i-1]){
			int temp=arr[i];
			int low=0;
			int high=i-1;
			while(low<=high){
				int mid=(low+high)/2;
				if(arr[mid] < temp){
					low=mid+1;
				}else{
					high=mid-1;
				}
			}
			//元素后移,为插入temp做准备
			for(int j=i;j>low;j--){
				arr[j]=arr[j-1];
			}
			arr[low]=temp;
		}
	}
}
  • 希尔排序

基本思路:最小增量排序,原理:先将待排序的数组元素分成多分子序列,使得每个子序列的元素个数相对较少,然后对各个子序列分别进行直接插入排序,待整个待排序序列“基本有序后”,再对所有元素进行一次直接插入排序。

//增量设置:d1 = n/2,d2 = d1/2 ...

//举例一下:{9,8,7,6,5,4,3,2,1,0} 10个数,现分为5组(9,4),(8,3),(7,2),(6,1),(5,0),然后分别对每组进行直接插入排序得到:(4,9),(3,8),(2,7),(1,6),(0,5),再将这5组分为2组(4,3,2,1,0),(9,8,7,6,5)分别对这两组进行直插排序,得:(0,1,2,3,4),(5,6,7,8,9)最终有序。

public class Sort{
	public static void shellSort(int arr[]){
		int len=arr.length;
		int i,j;
		int h;
		int temp;
		for(h=len/2;h>0;h=h/2){
			for(i=h;i<len;i++){
				j=i;
				temp=arr[j];
				while(j-h>=0 && arr[j-h]>temp){
					arr[j]=arr[j-h];
					j=j-h;
				}
				arr[j]=temp;
			}
		}
	}
}

 

交换排序

  • 冒泡排序

基本思路:每次比较相邻两个元素,如果它们的顺序错误就把它们交换

//第一层循环,总比较次数n-1:0-n~2(i<n-1)

//第二层循环,未归位的位置比较

public class bubbleSort{
    public static void bubbleSort(int[] array){
        if(null=array || array.length < 2){
            return;
        }
        for(int i=0;i<array.length-1;i++){
            for(int j=0;j<array.length-i-1;j++){
                if(a[j]>a[j+1]){
                    int temp=a[j];
                    a[j]=a[j+1];
                    a[j+1]=temp;
                }
            }
        }
        
    public static void main(String [] args){
        int[] array={10,9,8,7,6,5,4,3,2,1};
        bubbleSort(array);
        for(int num:array){
            System.out.print(num+" ");
        }
    }
}
  • 快速排序

基本思路:i和j,分别指向第一个和最后一个,i像后移动,j向前移动,选第一个数为标准(一般这样做,当然快排的关键就是这个“标准”的选取),从后面开始,找到第一个比标准小的数,互换位置,然后再从前面,找到第一个比标准大的数,互换位置,第一趟的结果就是标准左边的都小于标准,右边的都大于标准(但不一定有序),分成两拨后,继续递归的使用上述方法,最终有序!

//快速排序
//定义一个阈值,分别从最左面和最右面向中间遍历元素,左面找到一个大于阈值的数据便停止,
//右边找到一个小于阈值的数据便停止,如果此时左右两边都还没有走到中间,
//则交换左面大于阈值的数据和右面小于阈值的数据;重复上述过程,直到左面指针和右面指针相遇,
//此时左面数据均小于阈值,右面数据均大于阈值,划分结束。划分结束后,数据仍然是无序的,
//但更接近于有序。
public class quickSort{
	public static void sort(int arr[],int low,int high){
		int i,j;
		int index;
		if(null=arr||low>=high)
			return;
		i=low;
		j=high;
		index=array[i];
		while(i<j){
			while(i<j && arr[j]>=index)
				j--;
			if(i<j)
				arr[i++]=arr[j];
			while(i<j && arr[i]<=index)
				i++;
			if(i<j)
				arr[j--]=arr[i];
		}
		arr[i]=index;
		sort(array,low,i-1);
		sort(array,i+1,high);
	}
	public static void quickSort(int arr[]){
		sort(arr,0,arr.length-1)
	}
}

 

选择排序

  • 简单选择排序

基本思路:

//首先找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置

//如果第一个元素就是最小元素,那么它就和自己交换。如果只剩下最后一个元素,就没必要排了,它就是最大的

//再次在剩下的元素中找最小的元素,将它与数组中的第二个元素交换。如此往复,直到将整个数组排序。

public static void selectSort(int[] arr) {
    //数组是空或者数组里面只有一个元素,没必要排序
    if (null == arr || arr.length < 2)
        return;
    //最后一个元素没必要再给它排序,因为它就是最大的
    for (int i = 0; i < arr.length - 1; i++) {
        //初始的时候就认为最小值索引指针在i位置,最小值就是arr[i]
        int minIndex = i;
        //在I后面[i+1,arr.length-1]区间找最小的值
        for (int j = i + 1; j < arr.length; j++) {
            //找到就把最小值索引更新,找不到最小值索引还在原位
            minIndex = arr[j] < arr[minIndex] ? j : minIndex;
        }
        //将i位置上的元素值和最小索引位置的元素值交换
        int temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
        }
    }
}

 

  • 堆排序

相关知识:完全二叉树:叶子结点只可能在层次最大的两层上出现.,并且最下面一层节点都集中在该层最左边的若干位置的二叉树。堆是具有如下性质的二叉树:每个结点的值都小于(大于)或等于其左右孩子结点的值,称为小(大)顶堆。

Ki<=K2i; Ki<=K2i+1

<<左移运算符,num << 1,相当于num乘以2

>>右移运算符,num >> 1,相当于num除以2

基本思路:对一组待排序记录的键值,首先把它们按照堆的定义排成一个堆,称这一过程为建堆。首先找到最小(大)键值,然后将最小(大)键值取出,用剩下的键值再建堆,取得次小(大)的键值。如此反复进行,直到得到最大(小)键值,从而将全部键值排好序为止。

堆排序分为三个过程:

1. 建堆:从一个数组顺序读取元素,建立一个堆(完全二叉树)

2. 初始化:将堆进行调整,使得堆顶为最大(最大堆)或者最小(最小堆)的元素

3. 维护:将堆顶元素出堆后,需要将堆的最后一个节点补充到堆顶,因为这样破坏了堆的秩序,所以需要进行维护。

 

public class HeapSort{
	public static int[] sort=new int[] {1,0,10,20,3,5,6,4,9,8,12,17,34,11};
	public static void main(String[] args){
		buildMaxHeapify(sort);
		heapSort(sort);
	}
	public static void  buildMaxHeapify(int[] arr){
		int startIndex=getParentIndex(arr.length-1);
		for(int i=startIndex;i>=0;i--){
			maxHeapify(arr,arr.length,i);
		}
	}
	public static void maxHeapify(int[] arr,int heapSize,int index){
		int left=getChildLeftIndex(index);
		int right=getChildRightIndex(index);
		
		int largest=index;
		if(left<heapSize && arr[index] < arr[left]){
			largest=left;
		}
		if(right<heapSize&& arr[largest] < arr[right]{
			largest=right;
		}
		//得到最大值后可能需要交换,如果交换了,其子节点可能就不是最大堆了,需要重新调整
		if(largest!=index){
			int temp=arr[index];
			arr[index]=arr[largest];
			arr[largest]=temp;
			maxHeapify(arr,heapSize,largest);
		}
	}
	//排序,最大值放在末尾
	private static void heapSort(int arr[]){
		for(int i=arr.length-1;i>0;i--){
			int temp=arr[0];
			arr[0]=arr[i];
			arr[i]=temp;
			maxHeapify(arr,i,0);
		}
	}
	//父结点位置
	private static int getParentIndex(int current){
		return (current-1) >> 1;
	}
	//左子结点位置
	private static int getChildLeftIndex(int current){
		return (current << 1)+1;
	}
	//右子结点位置
	private static int getChildRightIndex(int current){
		return (current << 1)+2;
	}
	
}

其他排序

  • 归并排序

基本思路:归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。

public class Sort{
	public static void Merge(int arr[],int p,int q,int r){
		int i,j,k,n1,n2;
		n1=q-p+1;
		n2=r-q;
		int[] L=new int[n1];
		int[] R=new int[n2];
		for(i=0,k=p;i<n1;i++,k++)
			L[i]=array[k];
		for(j=0,k=q+1;j<n2;j++,k++)
			R[j]=array[k];
		for(k=p,i=0,j=0;i<n1 && j<n2;k++){
			if(L[i]<R[j]){
				arr[k]=L[i++];
			}else{
				arr[k]=R[j++];
			}
		}
		while(i<n1){
			arr[k++]=L[i++]
		}
		while(j<n2){
			arr[k++]=R[j++]
		}
	}
	public static vois MergeSort(int arr[],int p,int r){
		if(p<r){
			int q=(p+r)/2;
			MergeSort(arr,p,q);
			MergeSort(arr,q+1,r);
			Merge(arr,p,q,r);
		}
	}
	public Static void main(String[] args){
		int i=0;
		int a[]={5,4,9,8,7,6,0,1,3,2};
		int len=a.length;
		MergeSort(a,0,len-1);
	}
}
  • 计数排序

基本思路:对每一个输入的元素arr[i],确定小于 arr[i] 的元素个数。
所以可以直接把 arr[i] 放到它输出数组中的位置上。假设有5个数小于 arr[i],所以 arr[i] 应该放在数组的第6个位置上。

算法流程(1)

需要三个数组:

//待排序数组 int[] arr = new int[]{4,3,6,3,5,1};

//辅助计数数组 int[] help = new int[max - min + 1]; //该数组大小为待排序数组中的最大值减最小值+1

//输出数组 int[] res = new int[arr.length];

1.求出待排序数组的最大值max=6, 最小值min=1

2.实例化辅助计数数组help,help数组中每个下标对应arr中的一个元素,help用来记录每个元素出现的次数

3.计算 arr 中每个元素在help中的位置 position = arr[i] - min,此时 help = [1,0,2,1,1,1]; (3出现了两次,2未出现)

4.根据 help 数组求得排序后的数组,此时 res = [1,3,3,4,5,6]

public static int[] countSort1(int[] arr){
    if (arr == null || arr.length == 0) {
        return null;
    }    
    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;    
    //找出数组中的最大最小值
    for(int i = 0; i < arr.length; i++){
        max = Math.max(max, arr[i]);
        min = Math.min(min, arr[i]);
    }    
    int help[] = new int[max];    
    //找出每个数字出现的次数
    for(int i = 0; i < arr.length; i++){
        int mapPos = arr[i] - min;
        help[mapPos]++;
    }    
    int index = 0;
    for(int i = 0; i < help.length; i++){
        while(help[i]-- > 0){
            arr[index++] = i+min;
        }
    }    
    return arr;
}

算法流程(2)

需要三个数组:

//待排序数组 int[] arr = new int[]{4,3,6,3,5,1};

//辅助计数数组 int[] help = new int[max - min + 1]; //该数组大小为待排序数组中的最大值减最小值+1

//输出数组 int[] res = new int[arr.length];

1.求出待排序数组的最大值max=6, 最小值min=1

2.实例化辅助计数数组help,help用来记录每个元素之前出现的元素个数

3.计算 arr 每个数字应该在排序后数组中应该处于的位置,此时 help = [1,1,4,5,6,7];

4.根据 help 数组求得排序后的数组,此时 res = [1,3,3,4,5,6]

public static int[] countSort2(int[] arr){
    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;    
    //找出数组中的最大最小值
    for(int i = 0; i < arr.length; i++){
        max = Math.max(max, arr[i]);
        min = Math.min(min, arr[i]);
    }    
    int[] help = new int[max - min + 1];    
    //找出每个数字出现的次数
    for(int i = 0; i < arr.length; i++){
        int mapPos = arr[i] - min;
        help[mapPos]++;
    }    
    //计算每个数字应该在排序后数组中应该处于的位置
    for(int i = 1; i < help.length; i++){
        help[i] = help[i-1] + help[i];
    }    
    //根据help数组进行排序
    int res[] = new int[arr.length];
    for(int i = 0; i < arr.length; i++){
        int post = --help[arr[i] - min];
        res[post] = arr[i];
    }    
    return res;
}
  • 基数排序

基本思路:将整数按位数切割成不同的数字,然后按每个位数分别比较。

具体做法:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

public class RadixSort {
    /*
     * 获取数组a中最大值
     *
     * 参数说明:
     *     a -- 数组
     *     n -- 数组长度
     */
    private static int getMax(int[] a) {
        int max;
        max = a[0];
        for (int i = 1; i < a.length; i++)
            if (a[i] > max)
                max = a[i];
        return max;
    }
    /*
     * 对数组按照"某个位数"进行排序(桶排序)
     *
     * 参数说明:
     *     a -- 数组
     *     exp -- 指数。对数组a按照该指数进行排序。
     *
     * 例如,对于数组a={50, 3, 542, 745, 2014, 154, 63, 616};
     *    (01) 当exp=1表示按照"个位"对数组a进行排序
     *    (02) 当exp=10表示按照"十位"对数组a进行排序
     *    (03) 当exp=100表示按照"百位"对数组a进行排序
     *    ...
     */
    private static void countSort(int[] a, int exp) {
        //int output[a.length];    // 存储"被排序数据"的临时数组
        int[] output = new int[a.length];    // 存储"被排序数据"的临时数组
        int[] buckets = new int[10];

        // 将数据出现的次数存储在buckets[]中
        for (int i = 0; i < a.length; i++)
            buckets[ (a[i]/exp)%10 ]++;

        // 更改buckets[i]。目的是让更改后的buckets[i]的值,是该数据在output[]中的位置。
        for (int i = 1; i < 10; i++)
            buckets[i] += buckets[i - 1];

        // 将数据存储到临时数组output[]中
        for (int i = a.length - 1; i >= 0; i--) {
            output[buckets[ (a[i]/exp)%10 ] - 1] = a[i];
            buckets[ (a[i]/exp)%10 ]--;
        }

        // 将排序好的数据赋值给a[]
        for (int i = 0; i < a.length; i++)
            a[i] = output[i];

        output = null;
        buckets = null;
    }

    /*
     * 基数排序
     *
     * 参数说明:
     *     a -- 数组
     */
    public static void radixSort(int[] a) {
        int exp;    // 指数。当对数组按各位进行排序时,exp=1;按十位进行排序时,exp=10;...
        int max = getMax(a);    // 数组a中的最大值

        // 从个位开始,对数组a按"指数"进行排序
        for (exp = 1; max/exp > 0; exp *= 10)
            countSort(a, exp);
    }

    public static void main(String[] args) {
        int i;
        int a[] = {53, 3, 542, 748, 14, 214, 154, 63, 616};

        System.out.printf("before sort:");
        for (i=0; i<a.length; i++)
            System.out.printf("%d ", a[i]);
        System.out.printf("\n");

        radixSort(a);    // 基数排序

        System.out.printf("after  sort:");
        for (i=0; i<a.length; i++)
            System.out.printf("%d ", a[i]);
        System.out.printf("\n");
    }
}
  • 桶排序

桶排序可用于最大最小值相差较大的数据情况,比如[9012,19702,39867,68957,83556,102456]。

但桶排序要求数据的分布必须均匀,否则可能导致数据都集中到一个桶中。比如[104,150,123,132,20000], 这种数据会导致前4个数都集中到同一个桶中。导致桶排序失效。

基本思路:把数组 arr 划分为n个大小相同子区间(桶),每个子区间各自排序,最后合并。

计数排序是桶排序的一种特殊情况,可以把计数排序当成每个桶里只有一个元素的情况。

算法流程:

1.找出待排序数组中的最大值max、最小值min

2.我们使用 动态数组ArrayList 作为桶,桶里放的元素也用 ArrayList 存储。桶的数量为(max-min)/arr.length+1

3.遍历数组 arr,计算每个元素 arr[i] 放的桶

4.每个桶各自排序

5.遍历桶数组,把排序好的元素放进输出数组

//桶排序
public static void bucketSort(int[] arr){    
    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;
    for(int i = 0; i < arr.length; i++){
        max = Math.max(max, arr[i]);
        min = Math.min(min, arr[i]);
    }    
    //桶数
    int bucketNum = (max - min) / arr.length + 1;
    ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
    for(int i = 0; i < bucketNum; i++){
        bucketArr.add(new ArrayList<Integer>());
    }    
    //将每个元素放入桶
    for(int i = 0; i < arr.length; i++){
        int num = (arr[i] - min) / (arr.length);
        bucketArr.get(num).add(arr[i]);
    }    
    //对每个桶进行排序
    for(int i = 0; i < bucketArr.size(); i++){
        Collections.sort(bucketArr.get(i));
    }    
    System.out.println(bucketArr.toString());    
}

参考博客:

 

https://www.cnblogs.com/bjh1117/p/8335628.html
https://blog.csdn.net/zhangerqing/article/details/8831542
https://segmentfault.com/a/1190000013967025(希尔排序)
https://www.cnblogs.com/zer0Black/p/6169858.html(计数排序、桶排序)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值