排序算法整理

排序平均情况最好情况最坏情况稳定与否空间复杂度
冒泡排序O(N2)O(N)O(N2)稳定1
选择排序O(N2)O(N2)O(N2)不稳定1
直接插入排序O(N2)O(N)O(N2)稳定1
二分插入排序O(NlogN)O(log2N)O(N^2)稳定O(1)
希尔排序O(NlogN)(依赖于增量序列)(依赖于增量序列)不稳定1
快速排序O(NlogN)O(NlogN)O(N2)不稳定O(logN)
归并排序O(NlogN)O(NlogN)O(NlogN)稳定O(N)
二叉树排序O(NlogN)O(NlogN)O(N2)稳定O(N)
堆排序O(NlogN)O(NlogN)O(NlogN)不稳定1
拓扑排序O(N+E)O(N)

1、直接插入

这里写图片描述

如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。
代码示例:

public static int[] straightInsertSort(int[] nums){
		for(int i = 1; i < nums.length; i++){
			if(nums[i] < nums[i-1]){
				int j = i;
				int temp = nums[j];
				while(j > 0 && temp < nums[j-1]){
					nums[j] = nums[j-1];
					nums[j-1] = temp;
					j--;
					temp = nums[j];
				}
			}
		}
		return nums;
	}

此方法的时间复杂度是O(n^2)

2、二分插入排序

二分法查找其实就是折半查找,一种效率较高的查找方法。针对有需数组来查找的。
主要思想是:(设查找的数组期间为array[low, high])
(1)确定该期间的中间位置K
(2)将查找的值T与array[k]比较。若相等,查找成功返回此位置;否则确定新的查找区域,继续二分查找。区域确定如下:
a.array[k]>T 由数组的有序性可知array[k,k+1,……,high]>T;故新的区间为array[low,……,K-1]
b.array[k] < T 类似上面查找区间为array[k+1,……,high]。每一次查找与中间值比较,可以确定是否查找成功,不成功当前查找区间缩小一半。递归找,即可。
示例代码:

public static int[] binaryInsert(int[] nums){
		int high, low, mid;
		int temp;

		for(int i = 1; i < nums.length; i++){ // i = 4
			temp = nums[i];
			high = i-1;
			low = 0;
			while(low <= high){
				mid = (high + low) / 2;
				if(nums[mid] > temp){
					high = mid - 1;
				}else{
					low = mid + 1;
				}
			}
			for(int j = i ; j > low; j--){
				nums[j] = nums[j-1];
			}
			nums[low] = temp;
		}
		return nums;
	}

时间复杂度:O(log2n)

3、希尔排序

希尔排序时效分析很难,关键码的比较次数与记录移动次数依赖于增量因子序列d的选取,特定情况下可以准确估算出关键码的比较次数和记录的移动次数。目前还没有人给出选取最好的增量因子序列的方法。增量因子序列可以有各种取法,有取奇数的,也有取质数的,但需要注意:增量因子中除1 外没有公因子,且最后一个增量因子必须为1。
希尔排序方法是一个不稳定的排序方法。

这里写图片描述

示例代码:

public static int[] shellSort(int nums[]){  
   int dk = nums.length / 2;
   while(dk>=1){
	   nums = ShellInsertSort(nums, dk);
	   dk = dk / 2;
   }
   return nums;
} 

public static int[] ShellInsertSort(int[] nums, int dk){
	for(int i = dk; i < nums.length; i++){
		if(nums[i] < nums[i-dk]){
			int temp = nums[i];
			nums[i] = nums[i-dk];
			int j = i - dk;
			while(j >= 0 && temp < nums[j]){
				nums[j+dk] = nums[j];
				j = j - dk;
			}
			nums[j+dk] = temp;
		}
	}
	return nums;
} 

4、简单选择排序

第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换;
第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换;
以此类推…
第i 趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换,
直到整个序列按关键码有序。

public static int[] selectSort(int[] nums){
	int min;
	for(int i = 0; i < nums.length; i++){
		min = nums[i];
		int temp = i;
		for(int j = i + 1; j < nums.length; j++){
			if(min > nums[j]){
				temp = j;
				min = nums[j];
			}
		}
		nums[temp] = nums[i];
		nums[i] = min;
	}
	return nums;
}

时间复杂度O(N^2)

5、堆排序

建堆的核心内容是调整堆,使二叉树满足堆的定义(每个节点的值都不大于其父节点的值)。调堆的过程应该从最后一个非叶子节点开始,假设有数组A = {1, 3, 4, 5, 7, 2, 6, 8, 0}。那么调堆的过程如下图,数组下标从0开始,A[3] = 5开始。分别与左孩子和右孩子比较大小,如果A[3]最大,则不用调整,否则和孩子中的值最大的一个交换位置,在图1中是A[7] > A[3] > A[8],所以A[3]与A[7]对换,从图1.1转到图1.2。
每一次构建堆都是将此节点后面的所有节点重新排序一遍。
在这里插入图片描述

public static void sort(int[] arr) {
        //1.构建大顶堆
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            //从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(arr, i, arr.length);
        }
        //2.调整堆结构+交换堆顶元素与末尾元素
        for (int j = arr.length - 1; j > 0; j--) {
            swap(arr, 0, j);//将堆顶元素与末尾元素进行交换
            adjustHeap(arr, 0, j);//重新对堆进行调整
        }
    }
    
    /**
     * 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上
     * @param arr
     * @param i
     * @param length
     */
    public static void adjustHeap(int[] arr, int i, int length) {
        int temp = arr[i];//先取出当前元素i
        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {//从i结点的左子结点开始,也就是2i+1处开始
            if (k + 1 < length && arr[k] < arr[k + 1]) {//如果左子结点小于右子结点,k指向右子结点
                k++;
            }
            if (arr[k] > temp) {//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
                arr[i] = arr[k];
                i = k;
            } else {
                break;
            }
        }
        arr[i] = temp;//将temp值放到最终的位置
    }

    /**
     * 交换元素
     * @param arr
     * @param a
     * @param b
     */
    public static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
    
    public static void main(String []args){
        int []arr = {9,8,7,6,5,4,3,2,1};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }

6、快速排序

在这里插入图片描述

    public static Integer Partition(int[] a, int left, int right) {
        int key;
        key = a[left];

        while (left < right) {
            while (left < right && a[right] >= key) {
                right--;
            }
            a[left] = a[right];

            while (left < right && a[left] <= key) {
                left++;
            }
            a[right] = a[left];
        }

        a[left] = key;
        return left;
    }

    public static void QuickSort(int[] a, int left, int right) {
        if (left < right) {
            int pivotloc = Partition(a, left, right);
            QuickSort(a, left, pivotloc - 1);
            QuickSort(a, pivotloc + 1, right);
        }
    }
    
    public static void main(String[] args) {
        int[] a = new int[]{4, 1, 3, 2, 7, 6, 8};
        QuickSort(a, 0, a.length-1);
        System.out.println(a);
    }

详情请参考 https://www.jianshu.com/p/7631d95fdb0b

7、归并排序

1、先将数组分成左右两部分都是有序的数组
在这里插入图片描述
2、在两个有序数组基础上合并成一个数组的图解。
在这里插入图片描述
由此,就是整个流程。

    public static  int[] sort(int[] sourceArray) {

        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        if (arr.length < 2) {
            return arr;
        }
        //找到中间的点,然后一分为二
        int middle = (int) Math.floor(arr.length / 2);

        //将一个原始的数组original,从下标from开始复制,复制到上标to,生成一个新的数组 注意这里包括下标from,不包括上标to。
        int[] left = Arrays.copyOfRange(arr, 0, middle);

        int[] right = Arrays.copyOfRange(arr, middle, arr.length);
        //不停地去切分、
        return merge(sort(left), sort(right));
    }


    public static int[] merge(int[] left, int[] right) {

        int[] result = new int[left.length + right.length];//新数组,用于合并
        int i = 0;

        while (left.length > 0 && right.length > 0) {
            if (left[0] <= right[0]) {
                result[i++] = left[0];
                left = Arrays.copyOfRange(left, 1, left.length);//剪除第一个元素,刷新left数组
            } else {
                result[i++] = right[0];
                right = Arrays.copyOfRange(right, 1, right.length);//剪除第一个元素,刷新right数组
            }
        }

        while (left.length > 0) {
            result[i++] = left[0];
            left = Arrays.copyOfRange(left, 1, left.length);
        }

        while (right.length > 0) {
            result[i++] = right[0];
            right = Arrays.copyOfRange(right, 1, right.length);
        }

        return result;
    }

8、二叉树排序

先构建二叉树,然后进行中序遍历,即得到了排序值。
参考:https://blog.csdn.net/u011514810/article/details/52464130

     public class BinaryTree{
        int val;//根节点数据
        BinaryTree left;//左子树
        BinaryTree right;//右子树
        public BinaryTree(int val){
            this.val = val;
            this.left = null;
            this.right = null;
        }
    }

    public static void insert(BinaryTree root, int data){
        //二叉排序树的右节点都比根节点大
        if(data > root.val){
            if(root.right == null)
                root.right = new BinaryTree(data);
            else
                insert(root.right,data);//递归插入子节点
        }
        //二叉排序树的左节点都比根节点小
        else{
            if(root.left == null)
                root.left = new BinaryTree(data);
            else
                insert(root.left, data);//递归插入子节点
        }
    }

    public static void inOrder(BinaryTree root){
        if(root != null){
            inOrder(root.left);
            System.out.print(root.val+"-");
            inOrder(root.right);
        }
    }
    
    public static void main(String[] args) {
        int[] a = new int[]{4, 1, 3, 2, 7, 6, 8};
        BinaryTree root = new BinaryTree(a[0]);
        for(int i = 1; i<a.length; i++){
            insert(root, a[i]);
        }
        inOrder(root);
    }

9、冒泡排序

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

10、拓扑排序

在计算机科学领域,有向图顶点的线性排序就是其拓扑排序,例如,图形的顶点可以表示要执行的任务,并且边可以表示一个任务必须在另一个任务之前执行的约束; 在这个应用中,拓扑排序只是一个有效的任务顺序。当且仅当图形没有定向循环,即如果它是有向无环图(DAG),则拓扑排序是可能的。 任何 DAG 具有至少一个拓扑排序,并且已知这些算法用于在线性时间内构建任何 DAG 的拓扑排序。
拓扑排序通常用来“排序”具有依赖关系的任务。

public class Graph {
    private int V;   // No. of vertices
    private LinkedList<Integer> adj[]; // Adjacency List

    //构造方法
    Graph(int v) {
        V = v;
        adj = new LinkedList[v];
        for (int i = 0; i < v; ++i)
            adj[i] = new LinkedList();
    }

    // 添加一个边到图中
    void addEdge(int v, int w) {
        adj[v].add(w);
    }

    // 被拓扑排序调用的DFS函数
    void topologicalSortUtil(int v, boolean visited[],
                             Stack stack) {
        // 标记当前节点为已访问.
        visited[v] = true;
        Integer i;

        // 递归访问它的所有邻接顶点
        Iterator<Integer> it = adj[v].iterator();
        while (it.hasNext()) {
            i = it.next();
            if (!visited[i])
                topologicalSortUtil(i, visited, stack);
        }

        // 把当前节点加入存放结果的栈中
        stack.push(new Integer(v));
    }

    // 拓扑排序
    void topologicalSort() {
        Stack stack = new Stack();

        // 标记所有节点为未访问
        boolean visited[] = new boolean[V];
        for (int i = 0; i < V; i++)
            visited[i] = false;

        for (int i = 0; i < V; i++)
            if (visited[i] == false)
                topologicalSortUtil(i, visited, stack);

        // 打印结果栈的内容
        while (stack.empty() == false)
            System.out.print(stack.pop() + " ");
    }

    public static void main(String args[]) {
        Graph g = new Graph(6);
        g.addEdge(5, 2);
        g.addEdge(5, 0);
        g.addEdge(4, 0);
        g.addEdge(4, 1);
        g.addEdge(2, 3);
        g.addEdge(3, 1);
        g.topologicalSort();
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值