算法基础复习-排序

Java 主要排序方法为 java.util.Arrays.sort()

  • 对基本数据类型使用三向切分的快速排序
  • 对引用类型使用归并排序

对实体类排序,需要实现 Comparable 接口

排序定义

public class Sort<T extends Comparable<T>> {}

实体类定义

public class Pojo implements Comparable<Pojo>, Serializable {
	private int id;
	...
	@Override
    public int compareTo(Pojo p) {
        return Integer.compare(this.id, p.id);
    }
}

渐进符号

在这里插入图片描述
递归

  • 递归总有一个最简单的情况,方法的第一条语句总是一个包含 return 的条件语句
  • 递归调用总是去尝试解决一个规模更小的子问题
  • 递归调用的父问题和尝试解决的子问题之间不应该有交集

1. 冒泡排序

  1. 从上到下不断比较、交换相邻的两个元素
  2. 经过一轮的循环后,最大元素沉到最底下
  3. 进行下一轮循环比较,把第二大元素沉到倒数第二位置
  4. 依次类推
public class BubbleSort {

    public void bst(int[] array){

        int i, j;

        for(i = 0; i < array.length - 1; i++) {
            for(j = 0; j < array.length - 1 - i; j++) {
                if(array[j] > array[j+1]) {
                    int tmp;
                    tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                }
            }
        }
    }
}

2. 快速排序

最好情况:O(nlogn)
最坏情况:O(n2) 发生在已经排好序或者逆序,即 pivot 一侧是空的

  1. 随机选择一个元素作为基准,建立分离函数使左边元素都小于基准,右边元素都大于基准,在没有优化的情况下,一般直接选第一个元素作为基准即可
  2. 分离函数从最低端开始依次与基准元素对比,遇到大于基准的元素则交换位置;再从最高端开始依次与基准元素对比,小于基准元素则交换位置
  3. 经过重复多次3过程,最低端和最高端下标最终相等,此时的下标即基准位置
  4. 分别对基准左边和基准右边两个序列递归调用快速排序
  5. 最坏的情况下,第一次从最小的元素切分,第二次从第二小的元素切分等等,即数组最开始就是有序的,在进行快速排序前可以先随机打乱数组,可以使用shuffle函数打乱数组
public class QuickSort {

    public void qs(int[] array, int low, int high) {

        int pivot;	//pivot必须定义为局部变量,随当前方法销毁

        if (low >= high) 
        	return -1;
        	
        pivot = partition(array, low, high);
        qs(array, low, pivot - 1);
        qs(array, pivot + 1, high);

    }

    public int partition(int[] array, int low, int high) {

        int pivotKey = array[low];
        int tmp;

        while (low < high) {
            while (low < high && array[high] >= pivotKey)
                high--;
            tmp = array[high];
            array[high] = array[low];
            array[low] = tmp;

            while (low < high && array[low] <= pivotKey)
                low++;
            tmp = array[low];
            array[low] = array[high];
            array[high] = tmp;
        }
        return low;
    }
}

3. 插入排序

最坏情况:O(n2)

  1. 把要插入的元素存放到一个临时位置作为哨兵,可被作为哨兵的元素用 r[i] 表示
  2. 哨兵从第二个位置,即 i=2 开始选择,当比前一个位置的元素小时,就作为哨兵,否则后移一位,即 i++ 后继续判断
  3. 把选出的元素临时存到r[0],此时r[0]就是哨兵
  4. 从哨兵元素位置开始,依次比较前一个元素和哨兵元素的大小,大于哨兵的元素均后移一位,直到不大于哨兵元素时停止移动
  5. 完成移动后,把临时位置存的哨兵元素存入正确的位置 r[j + 1]
  6. 每完成一次哨兵插入,就 i++ 选取新的哨兵重复前边的操作
public class InsertSort {

    public void ist(int[] array) {

        int i, j;

        for (i = 1; i <= array.length - 1; i++) {

            if (array[i] < array[i-1]) {

                int tmp = array[i];

                for(j = i - 1; j >= 0 && array[j] > tmp ; j--){
                    array[j+1] = array[j];
                }

                array[j+1] = tmp;

            }
        }
    }
}

4. 希尔排序

  1. 希尔排序是插入排序的改进,插入排序选出哨兵后,每次前移一位与哨兵比较,希尔排序选出哨兵后,每次前移increment位与哨兵比较
  2. 设置合理的increment增量计算公式是关键,一般用increment=increment/大于2的整数,保证increment可以随循环逐渐递减到1
public class ShellSort {

    public void sst(int[] array) {

        int i, j;
        int increment = array.length;

        do {

            increment = increment / 3;

            for (i = increment; i <= array.length - 1; i++) {

                if (array[i] < array[i - increment]) {

                    int tmp = array[i];

                    for (j = i - increment; j >= 0 && array[j] > tmp; j -= increment)
                        array[j + increment] = array[j];

                    array[j + increment] = tmp;
                }
            }
        }
        while (increment > 0) ;
    }
}

5. 归并排序

最好、平均、最坏都是:nlogn

在这里插入图片描述
Merge:比较顶部元素大小,依次放入较小的元素,最后剩余的元素,逐个拼接到合并数组的后面
在这里插入图片描述

C

void MergeSort(SqList *L)
{
	MSort(L->r, L->r, 1, L->length);
}

void MSort(int SR[], int TR1[], int s, int t)
{
	int m;
	int TR2[MAXSIZE + 1]; //TR2作为辅助空间存放分裂后的新数组
	if (s == t)
		TR1[s] = SR[s];
	else
	{
		m = (s + t) / 2;
		MSort(SR, TR2, s, m); //分裂后的左部分递归再次分裂
		MSort(SR, TR2, m + 1, t); //分裂后的右部分递归再次分裂
		Merge(TR2, TR1, s, m, t); //归并:排序后合并
	}
}

void Merge(int SR[], int TR[], int i, int m, int n)
{
	int j, k, l; //j表示右部分的当前下标,范围[m+1,n];左部分范围[i,m];k表示归并后数组的当前下标
	for (j = m + 1, k = i; i <= m && j <= n; k++)
	{
		if (SR[i] < SR[j]) //比较当前左和右的顶部元素大小,较小的放入合并后数组
			TR[k] = SR[i++]; //如果左小于右,则左部分i位置元素存入归并数组
		else
			TR[k] = SR[j++]; //否则,如果右小于左,则有部分j位置元素存入归并数组
	}
	//当左部分或右部分中某部分完全遍历完,另一个可能还未遍历完,此时再遍历剩余的部分续接到合并的数组
	if (i <= m) //如果左侧没遍历完
	{
		for (l = 0; l <= m - i; l++)
			TR[k + l] = SR[i + l]; //剩余的部分必定是已排好序的,直接拼接到后面就行
	}
	if (j <= n) //如果右侧没遍历完
	{
		for (l = 0; l <= n - j; l++)
			TR[k + l] = SR[j + l];
	}
}

Java

public class MergeSort {

    public void mst(int[] array, int low, int high) {
        int mid = (low + high) / 2;
        if (low < high) {
            mst(array, low, mid);
            mst(array, mid + 1, high);
            merge(array, low, mid, high);
        }
    }

    public void merge(int[] array, int low, int mid, int high) {

        int[] tmp = new int[array.lengt

        int i = low;
        int j = mid + 1;
        int k = low;

        int x = low;

        while (i <= mid && j <= high) {
            if (array[i] < array[j]) {
                tmp[k++] = array[i++];
            } else {
                tmp[k++] = array[j++];
            }
        }

        while (i <= mid) {
            tmp[k++] = array[i++];
        }

        while (j <= high) {
            tmp[k++] = array[j++];
        }

        while (x <= high) {
            array[x] = tmp[x++];
        }

    }
}

可以看到归并排序应用的是分治策略

分治法

  1. 将原问题分解为几个规模较小但类似于原问题的子问题
  2. 递归求解这些子问题
  3. 最后合并子问题的解来建立原问题的解
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值