2.数组排序

1. 选择排序 O(N^2)

依次找到最小值的位置,然后与当前位置交换。比较完一圈之后再交换

public void selectSort(int[] arr) {
	for (int i = 0; i < arr.length; i++) {
	    int min = i;
	    for (int j = i + 1; j < arr.length; j ++) {
	        min = arr[min] > arr[j] ? j : min;
	    }
		swrap(arr, i, min);
	}
}
private void swrap(int[] arr, int a, int b) {
	int temp = arr[a];
	arr[a] = arr[b];
	arr[b] = temp;
}

2. 冒泡排序 O(N^2)

不断比较两个位置,小的放前面。每次比较都可能发生交换

public void popSort(int[] arr) {
	for (int i = 0; i < arr.length; i ++) {
		for (int j = i + 1; j < arr.length; j ++) {
			if (arr[i] > arr[j]) {
     			swrap(arr, i, j);
			}
		}
	}
}

3. 插入排序 O(N^2)

左侧的数相邻比较,直到左侧的数都有序为止。时间复杂度在数据情况交好时,需要的时间短

public void insertSort(int[] arr) {
	for (int i = 1; i < arr.length; i ++) {
		for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
			swrap(arr, j, j + 1);
		}
	}
}

4. 归并排序 O(N * log N)

分治思想。

  • 分割:递归地把当前序列平均分割成两半。
  • 集成:将上一步得到的子序列集成到一起(归并)。
public void sort2(int[] arr) {
        int step = 1, L, mid;
        while (step < arr.length) {
            L = 0;
            mid = L + step - 1;
            while (mid < arr.length) {
                merge(arr, L, mid, Math.min(mid + step, arr.length - 1));
                L = mid + step + 1;
                mid = L + step - 1;
            }
            if (step > arr.length / 2) {
                break;
            }
            step = step << 1;
        }
    }

5. 随机快速排序 O(N * log N)

    public void sort(int[] arr) {
        process(arr, 0, arr.length - 1);
    }
    
    private void process(int[] arr, int L, int R) {
        if (L > R) {
            return;
        }
        if (L == R) {
            return;
        }
        swrap(arr, (int)(Math.random() * (R - L + 1)) + L, R);
        int[] re = partition(arr, L, R);
        process(arr, L, re[0] - 1);
        process(arr, re[1] + 1, R);
    }
    
    public int[] partition(int[] arr, int L, int R) {
        int less = L, great = R - 1, i = L, X = arr[R];
        while (i <= great) {
            if (arr[i] < X) {
                swrap(arr, less++, i++);
            } else if (arr[i] > X) {
                swrap(arr, great--, i);
            } else {
                i++;
            }
        }
        swrap(arr, great + 1, R);
        return new int[]{less, great + 1};
    }

6. 堆排序 O(N * log N)

堆是一棵顺序存储的完全二叉树。
其中每个结点的value都不大于其孩子结点的value,这样的堆称为小根堆。
其中每个结点的value都不小于其孩子结点的value,这样的堆称为大根堆。

	// 插入堆后,向上进行比较,使其满足规则
    public void heapInsert(int[] arr, int i) {
        while (arr[(i - 1) / 2] < arr[i]) {
            swap(arr, (i - 1) / 2, i);
            i = (i - 1) / 2;
        }
    }
	// pop 堆后,向下比较,使其满足大根堆的规则
    public void heapify(int[] arr, int i, int size) {
        int left = 2 * i + 1;
        while (left < size) {
            int maxIndex = left + 1 < size && arr[left] < arr[left + 1] ? left + 1 : left;
            if (arr[maxIndex] > arr[i]) {
                swap(arr, i, maxIndex);
                i = maxIndex;
                left = 2 * i + 1;
            } else {
                break;
            }
        }
    }

    private void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public void sort(int[] arr) {
        if (arr.length <= 1) {
            return;
        }
        
        /* O(N * log N)
        int i = 0;
        while (i < arr.length) {
            heapInsert(arr, i++);
        }*/
        // O(N)
        int i = arr.length - 1;
        for (; i >= 0; i--) {
            heapify(arr, i, arr.length);
        }
        i = arr.length - 1;
        do {
            swap(arr, 0, i);
            heapify(arr, 0, i--);
        } while (i >= 0);
    }

基于非比较的排序。 桶排序

将数据放在容器中,然后排序

7. 计数排序 O(N)

如年龄排序,将所有的年龄存放到一个数组中,然后将数组取出

    private void sort(int[] ages) {
        if (ages.length <= 1) {
            return;
        }
        int[] help = new int[200];
        for (int i = 0; i < ages.length; i++) {
            help[ages[i]]++;
        }
        int i = 0;
        for (int j = 0; j < help.length; j++) {
            for (int k = 0; k < help[j]; k++) {
                ages[i++] = j;
            }
        }
    }

8. 基数排序

按照进制进行排序。 桶排序是按照基数进行排序,先个位再十位,再百位。。。,入桶的顺序和出桶的顺序需要保持一致;


    // 基数排序
    public void sort2(int[] arr) {
        if (arr.length <= 1) {
            return;
        }
        sortRadix(arr, 0, arr.length - 1, maxDigit(arr));
    }

    private void sortRadix(int[] arr, int L, int R, int d) {
        int radix = 10;
        int[] help = new int[radix];
        int[] helpSum = new int[radix];
        int[] copy = new int[R - L + 1];
        for (int i = 0; i < d; i++) {
            for (int j = L; j <= R; j++) {
                int currNum = getCurrNum(arr[j], i);
                help[currNum]++;
            }
            helpSum[0] = help[0];
            for (int j = 1; j < helpSum.length; j++) {
                helpSum[j] = helpSum[j - 1] + help[j];
            }
            for (int j = R; j >= L; j--) {
                int currNum = getCurrNum(arr[j], i);
                copy[helpSum[currNum] - 1] = arr[j];
                helpSum[currNum]--;
            }
            Arrays.fill(help, 0);
            System.arraycopy(copy, L, arr, L, R + 1 - L);
        }
    }

    private int getCurrNum(int num, int position) {
        int a = num % (int) Math.pow(10, position + 1);
        return a / (int) Math.pow(10, position);
    }

    private int maxDigit(int[] arr) {
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < arr.length; i++) {
            max = Math.max(arr[i], max);
        }
        int i = 1;
        while (max != 0) {
            max = max / (int) Math.pow(10, i++);
        }
        return i;
    }

总结

排序算法的稳定性

  • 稳定性是指同样大小的样本再排序之后不会改变相对次序
  • 对基础类型来说,稳定性毫无意义
  • 对非基础类型来说,稳定性有重要意义
  • 有些排序算法可以实现成稳定的,而有些排序算法无论如何都实现不成稳定的

在这里插入图片描述

1)不基于比较的排序,对样本数据有严格要求,不易改写
2)基于比较的排序,只要规定好两个样本怎么比大小就可以直接复用3)基于比较的排序,时间复杂度的极限是O(NlogN)
4)时间复杂度O(N
logN)、额外空间复杂度低于O(N)、且稳定的基于比较的排序是不存在的。
5)为了绝对的速度选 快排(常数时间小)、为了省空间选 堆排、为了稳定性选 归并

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值