常用排序算法

19 篇文章 0 订阅

基本概念介绍

  • 稳定排序
    对于待排序的数据,如果有元素a和b相等,如果排序前a在b的前面,在排序后a还是在b的前面,则称之为稳定排序。
  • 非稳定排序
    对于待排序的数据,如果有元素a和b相等,如果排序前a在b的前面,在排序后a可能出现在b的后面,则称之为非稳定排序。
  • 内排序
    待排文件的数据都是在内存储器中进行的。
  • 外排序
    如果数据很大,内存无法直接加载完成的数据,排序过程需要借助于内外存数据的交换来完成,则属于外排序

插入排序

直接插入排序
public class DirectInsertSorting {

    public static void main(String[] args) {
        int[] arr = new int[] {2,2,1,5,9,10,3,15,24};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }


    public static void sort(int[] arr) {
        if(arr == null || arr.length <= 1) {
            return;
        }
        int tmp;
        int j;
        for(int i = 1; i < arr.length; i++) {
            j = i - 1;
            tmp = arr[i];
            while (j >= 0 && tmp < arr[j]) {
                arr[j + 1] = arr[j];
                j--;
            }
            arr[j + 1] = tmp;
        }
    }
}
折半插入排序
public class BinaryInsertSorting {

    public static void main(String[] args) {
        int[] arr = new int[] {3,1,3,5,7,8,1,2};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void sort(int[] arr) {
        if(arr == null || arr.length <= 1) {
            return;
        }
        int lo, hi, mid, j;
        for(int i = 1; i < arr.length; i++) {
            lo = 0;
            hi = i - 1;
            int tmp = arr[i];
            while(lo <= hi) {
                mid = (lo + hi) >> 1;
                if(tmp < arr[mid]) {
                    hi = mid - 1;
                }else {
                    lo = mid + 1;
                }
            }
            for(j = i - 1; j >= lo; j--) {
                arr[j + 1] = arr[j];
            }
            arr[lo] = tmp;
        }

    }
}

折半插入排序主要是优化了查找应该插入元素的位置,但是移动元素的时间复杂度没有变化。

链表插入排序
/**
 * 链表插入排序就是改变指针的过程中容易出错
 */
public class LinkedInsertSorting {

    public static void main(String[] args) {
        Node head = new Node(-1, null);
        Node node1 = new Node(3, null);
        Node node2 = new Node(5, null);
        Node node3 = new Node(2, null);
        Node node4 = new Node(4, null);
        head.next = node1;
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        sort(head);
        Node p = head.next;
        while(p != null) {
            System.out.println(p.val);
            p = p.next;
        }
    }

    public static void sort(Node head) {
        if (head == null || head.next == null) {
            return;
        }

        Node p, q;
        p = head.next;
        q = head;
        q.next = null;
        while(p != null) {
            while(q.next != null && p.val > q.next.val) {
                q = q.next;
            }
            Node tmp = p.next;
            p.next = q.next;
            q.next = p;
            p = tmp;
            q = head;
        }
    }

    private static class Node {

        private Node(int val, Node next) {
            this.val = val;
            this.next = next;
        }

        int val;

        Node next;
    }
}

链表插入排序主要是优化了移动元素的时间复杂度,但是查找元素的时间复杂度并没有变化。

Shell排序
public class ShellInsertSorting {

    public static void main(String[] args) {
        int[] arr = new int[] {3,1,2,5,9,1};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void sort(int[] arr) {
        if(arr == null || arr.length <= 1){
            return;
        }

        int d;
        d = arr.length / 2;
        int len = arr.length;
        int i, j;
        while(d >= 1) {
            for(i = d; i < len; i++) {
                int tmp = arr[i];
                j = i - d;
                while(j >= 0 && arr[j] > tmp) {
                    arr[j + d] = arr[j];
                    j -= d;
                }
                arr[j + d] = tmp;
            }
            d /= 2;
        }
    }
}

shell排序稍微有点意思,通过跳跃性地对一些元素先进行排序,是时间复杂度降低,可以达到O(n^1.5)。

交换排序

最简单的交换排序是冒泡排序,优化的交换排序就是快排了。

冒泡排序
/**
 * 冒泡排序
 */
public class BubSort {

    public static void main(String[] args) {
        int[] arr = new int[] {1,3,5,8,3,2,1,10,7};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void sort(int[] arr) {
        if(arr == null || arr.length < 2) {
            return;
        }

        for(int i = 0; i < arr.length - 1; i++) {
            boolean isChange = false;
            for(int j = 0; j < arr.length - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    swap(arr, j, j + 1);
                    isChange = true;
                }
            }
            if(!isChange) {
                break;
            }
        }
    }

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

这个没啥好说的,符合人的直观思维,很好理解。

快排
/**
 * 快排
 */
public class quickSort {

    public static void main(String[] args) {
        int[] arr = new int[] {3,2,1,5,9,10,3};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void sort(int[] arr) {
        if(arr == null || arr.length < 2) {
            return;
        }
        sort(arr, 0, arr.length - 1);

    }

    private static void sort(int[] arr, int lo, int hi) {
        if(lo >= hi) {
            return;
        }
        int middle = getMiddle(arr, lo, hi);
        sort(arr, lo, middle - 1);
        sort(arr, middle + 1, hi);
    }

    private static int getMiddle(int[] arr, int lo, int hi) {
        if(lo >= hi) {
            return lo;
        }

        int val = arr[lo];
        while(lo < hi) {
            while(lo < hi && arr[hi] >= val) hi--;
            if(lo < hi) {
                arr[lo] = arr[hi];
            }
            while(lo < hi && arr[lo] <= val) lo++;
            if(lo < hi) {
                arr[hi] = arr[lo];
            }

        }
        arr[lo] = val;
        return lo;

    }
}

快排的核心是如何能够确定一个元素的位置,因为涉及到两个指针的移动,所以
有时容易出错。

选择排序

主要的就是直接选择排序和堆选择排序。

直接选择排序
/**
 * 直接选择排序
 */
public class DirectSelectSorting {

    public static void main(String[] args) {
        int[] arr = new int[] {1,3,5,2,1,4,2,9,7};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void sort(int[] arr) {
        if(arr == null || arr.length < 2) {
            return;
        }

        int len = arr.length;
        int i, j, k;
        for(i = 0; i < len - 1; i++) {
            k = i;
            for(j = i + 1; j < len; j++) {
                if(arr[j] < arr[k]) {
                    k = j;
                }
            }
            if(i != k) {
               swap(arr, i, k);
            }
        }
    }

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

堆选择排序
/**
 * 堆选择排序
 */
public class HeapSelectSorting {

    public static void main(String[] args) {
        int[] arr = new int[] {1,3,2,1,0,10,9,8};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void sort(int[] arr) {
        if(arr == null || arr.length < 2) {
            return;
        }
        int len = arr.length;
        int j;
        for(j = (len - 1) / 2; j >= 0; j--) {
            adjust(arr, j, len - 1);
        }
        for(j = len - 1; j >= 0; j--) {
            swap(arr, 0, j);
            adjust(arr, 0, j - 1);
        }


    }

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

    /**
     * 这个调整是再已满足堆定义的基础上的调整,所有有个初始化操作建立大根堆或者小根堆。
     * @param arr
     * @param s
     * @param n
     */
    private static void adjust(int[] arr, int s, int n) {
        int j = 2 * s + 1;
        int tmp = arr[s];
        while(j <= n) {
            if(j < n && arr[j + 1] > arr[j]) {
                j++;
            }
            if(arr[j] > tmp) {
                arr[s] = arr[j];
                s = j;
                j = 2 * s + 1;
            }else {
                break;
            }
        }
        arr[s] = tmp;
    }
}

堆选择排序核心是建立一个初始化堆和每次交换元素后重新调整为堆。

归并排序

import java.util.Arrays;

/**
 * @Author: jiangcw
 * @Date: 2019-7-28 下午 6:04
 * @Version 1.0
 *
 * 归并排序
 *
 *采用递归方式写,归并排序写法还是挺简单的,
 * 还是快排稍微难理解些
 */
public class MergeSorting {

    public static void main(String[] args) {
        int[] a = {50, 36, 66,76,95,12,25,36};
        sort(a);
        System.out.println(Arrays.toString(a));
    }

    public static void sort(int[] a) {
        if(a == null || a.length < 2) {
            return;
        }
        int[] tmp  = new int[a.length];
        merge(a, 0, a.length-1, tmp);
    }

    private static void merge(int[] a, int lo, int hi, int[] tmp) {
        if(lo >= hi) {
            return;
        }
        int mid = (lo + hi) >> 1;
        merge(a, lo, mid, tmp);
        merge(a, mid + 1, hi, tmp);
        int firstStart = lo;
        int secondStart = mid + 1;
        int j = lo;
        while(firstStart <= mid && secondStart <= hi) {
            if(a[firstStart] < a[secondStart]) {
                tmp[j++] = a[firstStart++];
            }else {
                tmp[j++] = a[secondStart++];
            }
        }
        if(firstStart <= mid) {
            while(firstStart <= mid) {
                tmp[j++] = a[firstStart++];
            }
        }else {
            while(secondStart <= hi) {
                tmp[j++] = a[secondStart++];
            }
        }
        while(lo <= hi) {
            a[lo] = tmp[lo];
            lo++;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值