排序算法-Java实现

155 阅读2分钟

排序算法Java实现

堆排序

public class HeapSort {

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

    /**
     * 大顶堆 每次都能选择一个最大的元素 , 然后将堆定元素移动到排序后的位置
     *
     * @param array
     */
    public static void heapSort(int[] array) {
        if (array == null || (array.length <= 1)) {
            return;
        }
        // 从后往前排
        for (int i = array.length - 1; 0 < i; i--) {
            adjustMaxHeap(array, i);
            swap(array, 0, i);
        }
    }

    public static void adjustMaxHeap(int array[], int length) {
        int parent = (length - 1) / 2; // 这里的完全二叉树的编号是从0开始
        for (int i = parent; 0 <= i; i--) {
            if (i * 2 + 2 <= length) { // 左右孩子都齐全
                // 注意第一个条件是大于等于  如果两个if的判断都使用<那么 当array[i * 2 + 1] == array[i * 2 + 2]无法和array[i]比较
                if (array[i * 2 + 1] <= array[i * 2 + 2] && array[i] < array[i * 2 + 2]) {
                    swap(array, i, i * 2 + 2);
                }
                if (array[i * 2 + 2] < array[i * 2 + 1] && array[i] < array[i * 2 + 1]) {
                    swap(array, i, i * 2 + 1);
                }
            } else { // 没有右孩子的情况
                if (array[i] <= array[i * 2 + 1]) {
                    swap(array, i, i * 2 + 1);
                }
            }
        }
    }


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

快速排序

public class QuickSort {

    public static void main(String[] args) {
        testQuick();
    }

    public static void testQuick() {
        int[] array = {10, 8, 11, 5, 2, 4, 3, 3, 1, 7};
        quickSort(array, 0, array.length - 1);
        System.out.println(Arrays.toString(array));
    }
    
    public static void quickSort(int[] array, int low, int high) {
        if (low < high) { // 这里需要注意 不是<=
            int location = partition(array, low, high);
            quickSort(array, low, location - 1);
            quickSort(array, location + 1, high);
        }
    }

    public static int partition(int[] array, int low, int high) {
        int e = array[low];
        int l = low;
        int h = high;
        while (l < h) { // 这里也需要注意 不是l<=h
            while (l < h && e <= array[h]) h--; // 这里的判断条件是 l < h 不是 0 < h
            array[l] = array[h];
            while (l < h && array[l] <= e) l++;
            array[h] = array[l];
        }
        array[l] = e;
        return l;
    }

}


双轴快速排序

public class DoubleQuick {

    public static void main(String[] args) {
        int[] array = {10, 8, 11, 5, 2, 10, 4, 12, 3, 1, 7, 4, 10};
        dualPivotQuickSort(array, 0, array.length - 1);
        System.out.println(Arrays.toString(array));
    }

    /**
     * 双轴快速排序
     *
     * @param array
     * @param start
     * @param end
     */
    public static void dualPivotQuickSort(int[] array, int start, int end) {
        /**
         * 注意array[start] 和 array[end]相等的场景,这个算法也能达到把和边界相等的元素向中间聚集的目的
         */
        if (start >= end) {
            return;
        }
        if (array[start] > array[end]) {
            swap(array, start, end);
        }
        int pivot1 = array[start]; // 左边界
        int pivot2 = array[end]; // 又边界

        int left = start; // 大于array[start] 的最右边元素的位置
        int right = end; // 大于array[end] 的最左边元素的位置
        int k = left + 1;
        while (k < right) { // 关键点1
            if (array[k] < pivot1) {
                swap(array, left + 1, k);
                left++;
                k++;
            } else if (array[k] <= pivot2) {
                k++;
            } else {
                while (array[right - 1] > pivot2) { // 保证左边元素参与调整时都>pivot2
                    if (right-- == k) break; // right-- 会在判断的时候就执行right = right - 1 ,原因是既然能够进入循环那么满足 array[right - 1] > pivot2 所以在if中执行right左移一位
                }
                if (k >= right) break;
                swap(array, k, right - 1); // 为什么这里是right-1,因为>=right位置的元素都是大于pivot2的元素,小于right的元素还是待排元素
            }
        }
        swap(array, start, left);
        swap(array, end, right);
        dualPivotQuickSort(array, start, left - 1);
        dualPivotQuickSort(array, left + 1, right - 1);
        dualPivotQuickSort(array, right + 1, end);
    }

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

}

直接插入排序

数组

public class DirectInsertSort {

    public static void main(String[] args) {
        int[] array = {10, 9, 5, 8, 7, 6, 2, 5, 4, 3, 2, 1};
        directInsertSort(array);
        System.out.println(Arrays.toString(array));
    }

    public static void directInsertSort(int[] array) {
        if (array == null || array.length <= 1) return;
        int j;
        int tmp;
        for (int i = 1; i < array.length; i++) {
            j = i - 1;
            tmp = array[i];
            while (0 <= j && tmp < array[j]) { // 要有0<=j,不然会出现空指针
                array[j + 1] = array[j];
                j--;
            }
            array[j+1] = tmp; // 这里是j+1
        }
    }
}

链表


/**
 * 链表的直接插入排序
 * 思路:就是将已排序链表、待排序元素、剩余待排序链表 分开 然后处理,如果一起处理会比较复杂。
 * 已排序元素:4->6->8->null
 * 待排序元素:7
 * 剩余待排序元素:10->12
 * 
 * 合起来看就是:4->6->8->7->10->12  
 * 
 */
public class DirectInsertListSort {
    
    public static void main(String [] args){
        ListNode head = directInsertListSort(getUnSortList());
        printList(head);
    }

    public static ListNode getUnSortList(){
        ListNode node1 = new ListNode(7);
        ListNode node2 = new ListNode(6);
        ListNode node3 = new ListNode(5);
        ListNode node4 = new ListNode(6);
        ListNode node5 = new ListNode(5);
        ListNode node6 = new ListNode(8);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        node5.next = node6;
        return node1;
    }

    public static void printList(ListNode head){
        while(head != null){
            System.out.print(head.val + " ");
            head = head.next;
        }
    }


    public static ListNode directInsertListSort(ListNode list){
        if(list == null || list.next == null) return list;
        ListNode head = list;
        ListNode tmp = list.next;
        ListNode move = head;
        ListNode pre = null;
        ListNode current = null;
        head.next = null; // 将已排序元素从原链表中分离出来
        while(tmp != null) {
            current = tmp; // current当前待排序元素
            tmp = tmp.next; // tmp指向下一个待排序元素
            current.next = null; // 将待排序元素 从待排序链表中分离出来单独处理
            while(move != null){
                if(move.val <= current.val) {
                    pre = move;
                    move = move.next;
                } else {
                    break;
                }
            } 
            if(pre == null){
                current.next = move; // 待排序元素位置的插入位置位于头部节点。
                head = current;
            } else {
                current.next = pre.next; // 待排序元素位置的插入位置位于非头部
                pre.next = current;
            }
            pre = null;
            move = head;
        }
        return head;
    }

}

class ListNode {
    
    public int val;
    
    public ListNode next;
    
    public ListNode(int x) {
        val = x;
        next = null;
    }
    
}

归并排序

数组

public class MergeSort {

    public static void main(String[] args) {
        int[] array = {5, 6, 4, 7, 1, 2, 3, 7, 4};
        mergeSort(array, 0, array.length - 1);
        System.out.println(Arrays.toString(array));
    }

    public static void mergeSort(int[] array, int low, int high) {
        if (low < high) { // 递归结束条件 为什么
            int mid = (high - low) / 2 + low; // 避免 low + high 导致内存溢出
            mergeSort(array, low, mid);
            mergeSort(array, mid + 1, high);
            merge(array, low, mid, high);
        }
    }
    /**
     * 使用辅助数组temp,在原数组array的基础上合并low,mid 和 mid+1,high 这两个有序数组
     *
     * @param array
     * @param low
     * @param mid
     * @param high
     */
    public static void merge(int[] array, int low, int mid, int high) {
        /**
         * 额外的空间 临时存储 合并两个两个有序数组的结果
         */
        int[] temp = new int[high - low + 1]; // 需要合并的元素个数 high - low + 1
        int l = low;
        int r = mid + 1;
        int p = 0;
        while (l <= mid && r <= high) { // 单链表合并两个有序数组,使得合并后的数组依然有序
            temp[p++] = array[l] < array[r] ? array[l++] : array[r++];
        }
        while (l <= mid) {
            temp[p++] = array[l++];
        }
        while (r <= high) {
            temp[p++] = array[r++];
        }
        // 将排序结果移回到原数组
        for (int i = 0; i < p; i++) {
            array[low++] = temp[i];
        }
    }

}

链表


public class MergeSort {

    /**
     * 对链表进行归并排序
     * @param head
     * @return
     */
    public static ListNode mergeSort(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                break;
            }
        }
        fast = slow.next;
        slow.next = null;
        ListNode p1 = mergeSort(head);
        ListNode p2 = mergeSort(fast);
        return mergeTwoLists(p1, p2);
    }

    /**
     * 合并两个有序链表
     * @param head
     * @return
     */
    public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null ) {
            return l2;
        }
        if (l2 == null ) {
            return l1;
        }
        ListNode l3 = new ListNode(0);
        ListNode p;
        p = l3;
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                p.next = l1;
                l1 = l1.next;
            } else {
                p.next = l2;
                l2 = l2.next;
            }
            p = p.next;
        }
        if (l1 != null) {
            p.next = l1;
        }
        if (l2 != null) {
            p.next = l2;
        }
        return l3.next;
    }

}

class ListNode {
    
    public int val;
    
    public ListNode next;
    
    public ListNode(int x) {
        val = x;
        next = null;
    }
    
}

基数排序

public class RadixSort {

    public static void main(String[] args) {
        int[] data = new int[]{1, 5, 6, 89, 1100, 192, 221, 12, 23};
        radixSort(data, 10, 4);
        System.out.println(Arrays.toString(data));
    }

    /**
     * 两个关键点:分组、分组之后如何重排元素顺序
     *
     * 1. 需要两个辅助数组 : tmp 大小和data数组一样, location 作为桶数组,存放元素的位置;
     *
     *
     * @param data  待排元素集合
     * @param radix 排序元素进制
     * @param d     最大数据的位数
     */
    public static void radixSort(int[] data, int radix, int d) {
        int[] tmp = new int[data.length]; // 辅助数组 用于每次排序后元素挪动
        int[] location = new int[radix]; // 10 分组数组 0 1 2 3 4 5 6 7 8 9 桶数组  默认每个位置的值为0
        for (int i = 0, rate = 1; i < d; i++) {
            Arrays.fill(location,0); // 这段代码需要每次对location清0
            System.arraycopy(data, 0, tmp, 0, data.length);// 将data中的元素复制到tmp中
            // 遍历待排元素进行分组,求出每个分组中元素的个数
            for (int j = 0; j < data.length; j++) {
                location[(tmp[j] / rate) % radix]++; // 统计每个分组中元素的个数,10进制的每一位就是一个分组
            }
            // 记录每个元素的位置
            for (int j = 1; j < radix; j++) {
                location[j] = location[j] + location[j - 1];
            }
            // 这里必须倒着来
            // 调整经过排序后,各个元素赋值到应该处于的位置
            for (int j = data.length - 1; j >= 0; j--) {
                // 之所以要倒着来每个位置的元素我们放的时候是倒着放的
                // 为什么要--location  数组是从0开始的 假设就两个元素。1 2
                // 计算后location[1] = 1,location[2]=2 。 排序的时候应该data[location[1] - 1] = 1,data[location[2] - 1] = 2
                data[--location[(tmp[j] / rate) % radix]] = tmp[j]; // 注意tmp[j]/rate不要使用data[j]/rate
            }
            rate = rate * radix;
        }
    }

}