排序算法

目录

 

1、冒泡排序

2、选择排序

3、插入排序

4、希尔排序 

5、归并排序

6、快速排序

7、堆排序


1、冒泡排序

比较相邻的元素。如果第一个比第二个大,就交换它们两个;

对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;

针对所有的元素重复以上的步骤,除了最后一个;

重复步骤1~3,直到排序完成。

public void bubbleSort(int[] A){
    for(int i=0;i<A.length-1;i++){
        for(int j=0;j<A.length-i-1;j++){
            if(A[j]>A[j+1]){
                int temp = A[j];
                A[j] = A[j+1];
                A[j+1] = temp;
            }
        }
    }
}

时间复杂度O(n^{2}^{}),空间复杂度O(1)。

2、选择排序

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。

然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

以此类推,直到所有元素均排序完毕。 

public static void selectionSort(int[] a) {
        int n = a.length;
        for (int i = 0; i < n; i++) {
            int k = i;
            // 找出最小值的下标
            for (int j = i + 1; j < n; j++) {
                if (a[j] < a[k]) {
                    k = j;
                }
            }
            // 将最小值放到未排序记录的第一个位置
            if (k > i) {
                int tmp = a[i];
                a[i] = a[k];
                a[k] = tmp;
            }
        }
    }

时间复杂度O(n^{2}^{}),空间复杂度O(1)。 

3、插入排序

从第一个元素开始,该元素可以认为已经被排序;

取出下一个元素,在已经排序的元素序列中从后向前扫描;

如果该元素(已排序)大于新元素,将该元素移到下一位置;

重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;

将新元素插入到该位置后;

重复步骤2~5。

public static void insertSort(int[] A){
	for(int i=1;i<A.length;i++){
		int temp = A[i];
		for(int j=i-1;j>=0;j--){
			if(A[j]>temp){
				A[j+1] = A[j];
                A[j] = temp;
			}else{
				A[j+1] = temp;
				break;
			}
		}
	}
}

时间复杂度O(n^{2}^{}),空间复杂度O(1)。 

4、希尔排序 

希尔排序的原理:

根据需求,如果你想要结果从小到大排列,它会首先将数组进行分组

然后将较小值移到前面,较大值移到后面,最后将整个数组进行插入排序

这样比起一开始就用插入排序减少了数据交换和移动的次数,可以说希尔排序是加强版的插入排序

public static void shellSort(int[] a){
	for(int increment=a.length/2;increment>0;increment/=2){
		for(int i=increment;i<a.length;i++){
			int temp = a[i];
			for(int j=i;j>=increment;j=j-increment){
				if(a[j-increment]>temp){
					a[j]=a[j-increment];
				}else{
					break;
				}
			}
			a[j]=temp;
		}
	}
}

最坏时间复杂度O(n^{2}^{}),最好时间复杂度O(n),空间复杂度O(1) 

5、归并排序

把长度为n的输入序列分成两个长度为n/2的子序列;

对这两个子序列分别采用归并排序;

将两个排序好的子序列合并成一个最终的排序序列

    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(nlogn),空间复杂度O(n)。

6、快速排序

从数列中挑出一个元素,称为 “基准”(pivot);

重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;

递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

    public static void quickSort(int[] numbers,int low,int high)
    {
        if(low < high)
        {
          int middle = getMiddle(numbers,low,high); //将numbers数组进行一分为二
          quickSort(numbers, low, middle-1);   //对低字段表进行递归排序
          quickSort(numbers, middle+1, high); //对高字段表进行递归排序
        }
    
    }

    public static int getMiddle(int[] numbers, int low,int high)
    {
        int temp = numbers[low]; //数组的第一个作为中轴
        while(low < high)
        {
            //下面这个判断是>=,如果没有=,会导致死循环,比如[5,1,1,2,0,0]这个用例
            while(low < high && numbers[high] >= temp)
            {
                high--;
            }
            numbers[low] = numbers[high];//比中轴小的记录移到低端
            while(low < high && numbers[low] <= temp)
            {
                low++;
            }
            numbers[high] = numbers[low] ; //比中轴大的记录移到高端
            }
            numbers[low] = temp ; //中轴记录到尾
            return low ; // 返回中轴的位置
    }

时间复杂度O(nlogn),空间复杂度O(logn)~O(n)。

7、堆排序

将数组构建成一个最大堆(也就是根节点最大);

取出根节点(交换堆顶和最后一个元素);

将剩下的数组元素在建成一个最大二叉堆,返回第2步,直到所有元素都被取光

import java.util.Arrays;

public class HeapSort {
    public static void main(String[] args){
        int[] nums = {49,38,65,97,76,13,27,49,78,34,12,64};
        //循环构建最大堆
        for(int i=0;i<nums.length-1;i++){
            buildMaxHeap(nums,nums.length-1-i);
            //交换堆顶和最后一个元素(当前最大的值放到数组末尾)
            swap(nums,0,nums.length-1-i);
            System.out.println(Arrays.toString(nums));
        }
    }

    public static void buildMaxHeap(int[] array,int lastIndex){
        /*
        二叉树规律:(根节点为节点 0 )
        节点 i 的左子节点为节点 2*i+1,右子节点为节点2*i+2
         */
        //从lastIndex处的节点(最后一个节点)的父节点开始
        for(int i=(lastIndex-1)/2;i>=0;i--){
            //k为当前正在被判断的节点
            int k = i;
            //节点 k 拥有子节点
            if(2*k+1<=lastIndex){
                //biggerIndex记录子节点中较大的一个
                int biggerIndex = 2*k+1;
                //如果存在右子节点,并且右子节点的值大于左子节点,更新biggerIndex
                if(biggerIndex+1<=lastIndex&&array[biggerIndex+1]>array[biggerIndex]){
                    biggerIndex++;
                }
                //如果节点 k 的值小于它较大子节点的值,则交换
                if(array[k]<array[biggerIndex]){
                    swap(array,k,biggerIndex);
                }
            }
        }
    }

    public static void swap(int[] array,int i,int j){
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

时间复杂度O(nlogn),空间复杂度O(1)

 

参考:https://www.cnblogs.com/onepixel/articles/7674659.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值