没长正的技术专栏 勤动手、多思考

算法之排序

2017-02-01
mzz

阅读:

2021-07-28

目的

程序 = 数据结构 + 算法

了解数据结构、算法思想、优缺点、合理运用到项目中, 常见算法:

时间复杂度:

O(1) -> HashMap

O(logn)-> 二叉树、二分法

O(n) -> for 循环

O(nlogn) -> for循环嵌套二叉树 、 Arrays.sort、快排

O(n2) -> for循环嵌套

时间复杂度由小 到大为:

O(1) < O(log2n) < O(n) < O(n log2n) <O(n2)<O(n3) …<O(2n)<O(n!)

一、算法之排序

1. 冒泡、选择、插入排序

时间复杂度: 冒泡 < 选择 < 插入

	/**
	 * (量级过10W时非常慢)
     * 冒泡排序(相邻数据比较)
     *
     * @param nums
     */
    public static void bubbleSort(int[] nums) {

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

    /**
     * 插入数据排序  (一轮可能会换多个)  从左到右再到左排序,再遍历... 一直到截止。
     * <p>
     * 核心: 指定节点(用插入节点与节点以前的数据进行排序)
     *
     * @param nums 集合
     */
    public static void insertSort(int[] nums) {
        int insertNode;
        int j;
        for (int i = 1; i < nums.length; i++) {
            insertNode = nums[i];
            j = i - 1;
            while (j >= 0 && insertNode < nums[j]) {
                nums[j + 1] = nums[j];
                j--;
            }
            nums[j + 1] = insertNode;
        }
    }

    /**
     * 选择排序  (一轮只换一个)
     * <p>
     * 核心:  确定当前节点,然后索引++节点后面排序,然后替换。
     *
     * @param nums 集合
     */
    public static void selectSort(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            int pos = i;
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[pos] > nums[j]) {
                    pos = j;
                }
            }
            if (pos != i) {
                int temp = nums[i];
                nums[i] = nums[pos];
                nums[pos] = temp;
            }
        }
    }

2.快速排序

  • 速度快 (百万、千万数据很快) 有点类似二分查找中的双指针
public class QuickSort {
    public static void main(String[] args) {
        int[] nums = new int[1000000];
        long start = System.currentTimeMillis();
        for (int k = 0; k < 10; k++) {
            for (int j = 0; j < nums.length; j++) {
                nums[j] = (int) (Math.random() * 1000);
            }
            quickSort(nums);
        }
        System.out.println("quick sort cost :" + (System.currentTimeMillis() - start));
    }

    public static void quickSort(int[] nums) {

        if (nums == null || nums.length == 0) {
            return;
        }
        qSort(nums, 0, nums.length - 1);

//        for(int i =0;i<nums.length;i++) {
//            System.out.println(nums[i]);
//        }
    }

    private static void qSort(int[] nums, int start, int end) {
        if (start >= end) {
            return;
        }
        //获取第一个数据排序区分左右两部分数据左边小于右边大于该数
        int pivot = nums[start];
        int left = start;
        int right = end;
        while (left <= right) {
            while (left <= right && nums[left] < pivot) {
                left++;
            }
            while (left <= right && nums[right] > pivot) {
                right--;
            }
            if (left <= right) {
                int tmp = nums[right];
                nums[right] = nums[left];
                nums[left] = tmp;
                left++;
                right--;
            }
        }
        //想象图形移动过程 递归左排右排
        qSort(nums, start, right);//为什么是right
        qSort(nums, left, end);
    }
}

3.合并排序

D&C 分而治之的思想,欧几里德算法(辗转相除法)   递归的思想

基线条件:数组为空或者只有一个元素时  (类似与初中时给函数并找规律)

实例:农场主分地问题  1680 X 640 --> 640 X 400 --> 240 X 400 --> 240 X 160 --> ..

将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
//合并排序
public class MergeSort {

    public static void main(String[] args) {

        int[] nums = new int[100000];
        long start = System.currentTimeMillis();
        for (int k = 0; k < 10; k++) {
            for (int j = 0; j < nums.length; j++) {
                nums[j] = (int) (Math.random() * 1000);
            }
            mergeSort(nums);
        }
        System.out.println("merge sort cost :" + (System.currentTimeMillis() - start));
    }

    //merge Sort
    public static void mergeSort(int[] nums) {
        int[] temp = new int[nums.length];
        mergeSortDetail(nums, 0, nums.length - 1, temp);
    }

    public static void mergeSortDetail(int[] nums, int start, int end, int[] temp) {
        if (start >= end) {
            return;
        }
        int mid = (start + end) / 2;
        mergeSortDetail(nums, start, mid, temp);
        mergeSortDetail(nums, mid + 1, end, temp);
        //思考:递归到最小维度时;分治思想 (最底层是两个数字比较大小或单个数字;然后再想上合并排序!2 6 7 /3 4)
        merge(nums, start, mid, end, temp);
    }

    public static void merge(int[] nums, int start, int mid, int end, int[] temp) {

        int left = start;
        int right = mid + 1;
        int index = start;
        while (left <= mid && right <= end) {
            if (nums[left] < nums[right]) {
                temp[index++] = nums[left++];   // temp[index] =nums[left]; index++; left++;
            } else {
                temp[index++] = nums[right++];
            }
        }
         //左边单个
        while (left <= mid) {
            temp[index++] = nums[left++];
        }
        //右边单个
        while (right <= end) {
            temp[index++] = nums[right++];
        }

        //替换 (将有序集合重新插入nums集合中)
        for (index = start; index <= end; index++) {
            nums[index]= temp[index];
        }
    }
}

4.深度优先搜索

​ 树(由上到下) —> 栈结构遍历

5.广度优先搜索

用图的查找算法,主要解决两类问题:

  <1.从节点A出发,有前往节点B的路径吗?
  <2.从节点A出发,前往节点B的哪条路径最短?(段数/边最少的路径)

实例:在人际圈中查找水果经销商 –你的人际圈(一度关系)、你朋友的人际圈(二度关系)、朋友的朋友的人际圈(三度关系)

6.狄克斯特拉

找出最快到达目的地的路径。(所用时间最少的路径)

区别广度优先搜索:找出段数最少的路径(尽量少转车而达到目的地的路径)

狄克斯特拉算法用于每条边都有关联数字的图,这些数字称为权重(weight)。

带权重的图称为加权图(weighted graph),不带权重的图称为非加权图(unweighted graph)。


**负权边** 时,该算法不使用,可采用[贝尔曼-福德算法(Bellman-Fordalgorithm)]

实例: 换钢琴

总结:

  要计算非加权图中的最短路径,可使用广度优先搜索。
  要计算加权图中的最短路径,可使用狄克斯特拉算法(只适应有向无环图)。

7.贪婪算法

选择每个最优,则全最优。(排课表、背包问题、集合覆盖问题、近似完全问题)   近似算法(NP完全问题)

这是一种近似最优的算法(背包问题:最多拿10千克重的物品,请选择价值最大的。 10kg 6000元的音响、4kg 5000元的笔记本电脑、6kg 2000元的手提琴,通过贪婪算法,结果会选出10kg 6000元的音响,而 后面两者之后大于6000元的音响的价值。)

8.动态规划

将问题分成各个小问题,先解决小问题,再逐步解决大问题。
(背包问题、最长公共子串)

9.K最近邻算法

1.创建推荐系统(特征抽取、回归<期望值>、挑选合适的特征)
2.机器学习(OCR<图像识别文字>、创建垃圾邮件过滤器<贝叶斯分类器>、股票市场预测<影响因数多>)

10.总结/扩展

1.二叉树
2.反向索引
3.傅里叶变换(分类、DNA分析、音乐识别)
4.并行计算
5.MapReduce
	<1.分布式算法
	<2.映射函数
	<3.归并函数
6.布隆过滤器和HyperLogLog
7.SHA算法
8.局部敏感的散列算法
9.Diffie-Hellman密钥交换
10.线性规划

参考

«算法图解»

合并排序

贝尔曼-福德算法(Bellman-Fordalgorithm)

算法示例

二分法查找-百度百科

快速排序-百度百科

K最近邻算法

深度/广度优先搜索


欢迎拍砖,多多交流,转载请注明出处:[没长正的技术专栏](http://blog.meizhangzheng.com) 如涉及侵权问题,请发送邮件到xsj34567@163.com,如情况属实本人将会尽快删除。


上一篇 算法之二分法

下一篇 算法扩展

Comments

Content