讀書筆記——漫畫算法(1) 數據結構與排序算法

  • 讀書筆記知識導航
  • 源代碼查閱方便
  • 這裏的代碼與JDK的實現還相差甚遠,只能算作算法/數據結構的實現思路展示,很多細節沒考慮...

0. 思維導圖

在這裏插入圖片描述

1. 代碼導航

數據結構可以分爲物理結構邏輯結構,所以這裏分兩個包,一個是phy,一個是logic;邏輯結構又分爲幾大類,如隊列,棧,樹
在這裏插入圖片描述

2. 數組(Array)

public class Array {

    private int[] data;

    private int size;

    private final static int DEFAULT_SIZE = 10;

    public Array() {
        this(DEFAULT_SIZE);
    }

    public Array(int capacity) {
        if(size < 0) {
            throw new IllegalArgumentException("Capacity must be >= 0");
        }
        data = new int[capacity];
    }

    public Array(int[] data) {
        Objects.requireNonNull(data);
        this.data = new int[data.length];
        while(size < data.length) {
            this.data[size] = data[size++];
        }
    }

    public int get(int index) {
        if(index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
        return data[index];
    }

    public void set(int index, int elem) {
        if(index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
        data[index] = elem;
    }

    public int size() {
        return size;
    }

    /**
     * Insert element into SArray
     * Time Complexity: O(N)
     *
     *  index
     *   ↓
     * | j | k | ... | z |
     *
     * |   | j | k | ... | z |
     * @param index
     * @param elem
     */
    public void insert(int index, int elem) {
        if(index < 0 || index > size) {
            throw new IndexOutOfBoundsException();
        }

        // 先判斷是否可擴容
        if(size >= data.length) {
            resize();
        }

        // 移動元素位置
        int i = size;
        while(i > index) {
            data[i] = data[--i];
        }
        data[i] = elem;
        size++;
    }

    /**
     * Time Complexity: O(N)
     *
     *  index
     *   ↓
     * | j | k | ... | z |
     *
     * @param index
     * @return
     */
    public int remove(int index) {
        if(index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }

        int retData = data[index];
        while(index <= size-2) {
            data[index] = data[++index];
        }
        size--;
        return retData;
    }

    private void resize() {
        // 擴容爲兩倍
        int[] newData = new int[size << 1];
        System.arraycopy(data, 0, newData, 0, size);
        data = newData;
    }

    @Override
    public String toString() {
        int[] arr = new int[size];
        System.arraycopy(data, 0, arr, 0, size);
        return Arrays.toString(arr);
    }

    /**
     * 測試數據結構SArray
     * @param args
     */
    public static void main(String... args) {
        Array array = new Array();
        array.insert(0, 3);
        array.insert(1, 7);
        array.insert(2, 9);
        array.insert(3, 5);
        array.insert(1, 6);
        array.remove(1);
        System.out.println(array);
    }
}

3. 鏈表

public class LinkedList {

    private Node head;

    private Node last;

    private int size;

    public LinkedList() { }

    public LinkedList(int[] data) {
        Objects.requireNonNull(data);
        Node p = head = new Node(data[size++]);
        while(size < data.length) {
            p.next = new Node(data[size++]);
            p = p.next;
        }
    }

    public Node get(int index) {
        if(index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
        Node node = head;
        for(int i = 0 ; i < index ; i++) {
            node = node.next;
        }
        return node;
    }

    public void insert(int index, int elem) {
        if(index < 0 || index > size) {
            throw new IndexOutOfBoundsException();
        }
        Node insertedNode = new Node(elem);
        if(size == 0) {
            // 插入第一個
            head = last = insertedNode;
        } else if(index == 0) {
            // 首部插入
            insertedNode.next = head;
            head = insertedNode;
        } else if(index == size) {
            // 尾部插入
            last.next = insertedNode;
            last = insertedNode;
        } else {
            // 插中間節點
            Node prev = get(index-1);
            insertedNode.next = prev.next;
            prev.next = insertedNode;
        }
        size++;
    }

    public int remove(int index) {
        if(index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
        Node removedNode = null;
        if(index == 0) {
            // 刪除頭
            removedNode = head;
            head = head.next;
        } else if(index == size-1) {
            // 刪除尾
            Node prev = get(index-1);
            removedNode = prev.next;
            prev.next = null;
            last = prev;
        } else {
            // 刪除中間節點
            Node prev = get(index-1);
            removedNode = prev.next;
            prev.next = removedNode.next;
            removedNode.next = null;
        }
        size--;
        return removedNode.data;
    }

    public int size() {
        return size;
    }

    private static class Node {
        int data;
        Node next;

        Node() { }

        Node(int data) {
            this.data = data;
        }
    }

    @Override
    public String toString() {
        Array sArray = new Array();
        Node node = head;
        for(int i = 0 ; node != null ; i++, node = node.next) {
            sArray.insert(i, node.data);
        }
        return sArray.toString();
    }

    /**
     * 測試數據結構SLinkedList
     * @param args
     */
    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        linkedList.insert(0, 3);
        linkedList.insert(1, 7);
        linkedList.insert(2, 9);
        linkedList.insert(3, 5);
        linkedList.insert(1, 6);
        linkedList.remove(0);
        System.out.println(linkedList);
    }
}

4. 棧(Stack)

4.1 ArrayStack

用數組實現的棧結構,這裏的棧結構,我們使用上文已經定義過的Array

public class ArrayStack {

    private Array stack;

    public ArrayStack() {
        stack = new Array();
    }

    public ArrayStack(int capacity) {
        stack = new Array(capacity);
    }

    public void push(int elem) {
        stack.insert(stack.size(), elem);
    }

    public int pop() {
        int ret;
        try{
            ret = stack.remove(stack.size()-1);
        } catch (Exception e) {
            throw new EmptyStackException();
        }
        return ret;
    }

    public static void main(String[] args) {
        ArrayStack stack = new ArrayStack();

        System.out.print("Push : ");
        for(int i = 0 ; i < 5 ; i++) {
            System.out.print(i + " ");
            stack.push(i);
        }

        System.out.print("\nPop  : ");
        for(int i = 0 ; i < 5 ; i++) {
            System.out.print(stack.pop() + " ");
        }

        // try a exceptional operation : EmptyStack
        // System.out.println(stack.pop());
    }
}

4.2 LinkedListStack

基於鏈表實現的棧結構

public class LinkedStack {

    private LinkedList stack;

    public LinkedStack() {
        stack = new LinkedList();
    }

    public void push(int elem) {
        stack.insert(stack.size(), elem);
    }

    public int pop() {
        int ret;
        try {
            ret = stack.remove(stack.size()-1);
        } catch (Exception e) {
            throw new EmptyStackException();
        }
        return ret;
    }

    public static void main(String[] args) {
        LinkedStack stack = new LinkedStack();

        System.out.print("Push : ");
        for(int i = 0 ; i < 5 ; i++) {
            System.out.print(i + " ");
            stack.push(i);
        }

        System.out.print("\nPop  : ");
        for(int i = 0 ; i < 5 ; i++) {
            System.out.print(stack.pop() + " ");
        }

        // try a exceptional operation : EmptyStack
        // System.out.println(stack.pop());
    }
}

5. 循環隊列

public class CyclicQueue {

    private int[] queue;

    private int font;

    private int rear;

    public CyclicQueue(int capacity) {
        queue = new int[capacity];
    }

    public void enQueue(int elem) {
        if((rear+1)%queue.length == font) {
            throw new RuntimeException("Full Queue...");
        }
        queue[rear] = elem;
        rear = (rear+1) % queue.length;
    }

    public int deQueue() {
        if(font == rear) {
            throw new RuntimeException("Empty Queue...");
        }
        int deQueueElem = queue[font];
        font = (font+1)%queue.length;
        return deQueueElem;
    }

    public static void main(String[] args) {
        CyclicQueue queue = new CyclicQueue(10);

        System.out.print("Push : ");
        for(int i = 0 ; i < 5 ; i++) {
            System.out.print(i + " ");
            queue.enQueue(i);
        }

        System.out.print("\nPop  : ");
        for(int i = 0 ; i < 5 ; i++) {
            System.out.print(queue.deQueue() + " ");
        }

        // try a exceptional operation : EmptyQueue
        // System.out.println(queue.deQueue());
    }

}

6. 二叉樹

這裏會用遞歸/非遞歸的方式實現二叉樹的先序,中序,後序;以及二叉樹層序遍歷

public class BinaryTree {

    private TreeNode root;

    private static Random random = new Random(System.currentTimeMillis());

    private BinaryTree(TreeNode root) {
        this.root = root;
    }

    public static BinaryTree newBinaryTree(LinkedList list) {
        TreeNode root = newTree(list);
        return new BinaryTree(root);
    }

    private static TreeNode newTree(LinkedList list) {
        if(list == null || list.size() <= 0) {
            return null;
        }
        TreeNode root = null;
        int data;
        if((data = list.remove(0)) != TreeNode.NULL) {
            root = new TreeNode(data);
            TreeNode leftTree, rightTree; ;
            if((leftTree = newTree(list)) != null) {
                root.left = leftTree;
            }
            if((rightTree = newTree(list)) != null) {
                root.right = rightTree;
            }
        }
        return root;
    }

    /**
     * 前序遍歷
     * @param consumer 函數式consumer
     */
    public void preOrderTraversal(Consumer<Integer> consumer) {
        Objects.requireNonNull(consumer);
        int rand = random.nextInt(3);
        if(rand == 0) {
            System.out.println("遞歸先序遍歷: ");
            preOrderTraversal(root, consumer);
        } else if(rand == 1) {
            System.out.println("非遞歸先序遍歷: ");
            preOrderTraversal0(root, consumer);
        } else {
            System.out.println("層序遍歷: ");
            preOrderTraversal1(root, consumer);
        }
    }

    private void preOrderTraversal(TreeNode root, Consumer<Integer> consumer) {
        if(root != null) {
            consumer.accept(root.data);
            preOrderTraversal(root.left, consumer);
            preOrderTraversal(root.right, consumer);
        }
    }

    private void preOrderTraversal0(TreeNode root, Consumer<Integer> consumer) {
        java.util.LinkedList<TreeNode> stack = new java.util.LinkedList<>();
        TreeNode node = root;
        while(node != null || !stack.isEmpty()) {
            while(node != null) {
                consumer.accept(node.data);
                stack.push(node);
                node = node.left;
            }
            if(!stack.isEmpty()) {
                node = stack.pop();
                node = node.right;
            }
        }
    }

    private void preOrderTraversal1(TreeNode root, Consumer<Integer> consumer) {
        Queue<TreeNode> q = new java.util.LinkedList<>();
        q.offer(root);
        while(!q.isEmpty()) {
            TreeNode node = q.poll();
            consumer.accept(node.data);
            if(node.left != null) {
                q.offer(node.left);
            }
            if(node.right != null) {
                q.offer(node.right);
            }
        }
    }

    /**
     * 中序遍歷
     * @param consumer
     */
    public void inOrderTraversal(Consumer<Integer> consumer) {
        Objects.requireNonNull(consumer);
        if (random.nextBoolean()) {
            System.out.println("遞歸中序遍歷: ");
            inOrderTraversal(root, consumer);
        } else {
            System.out.println("非遞歸中序遍歷: ");
            inOrderTraversal0(root, consumer);
        }
    }

    private void inOrderTraversal(TreeNode root, Consumer<Integer> consumer) {
        if(root != null) {
            inOrderTraversal(root.left, consumer);
            consumer.accept(root.data);
            inOrderTraversal(root.right, consumer);
        }
    }

    private void inOrderTraversal0(TreeNode root, Consumer<Integer> consumer) {
        java.util.LinkedList<TreeNode> stack = new java.util.LinkedList<>();
        TreeNode node = root;
        while(node != null || !stack.isEmpty()) {
            while(node != null) {
                stack.push(node);
                node = node.left;
            }
            if(!stack.isEmpty()) {
                node = stack.pop();
                consumer.accept(node.data);
                node = node.right;
            }
        }
    }

    public void postOrderTraversal(Consumer<Integer> consumer) {
        Objects.requireNonNull(consumer);
        if (random.nextBoolean()) {
            System.out.println("遞歸後序遍歷: ");
            postOrderTraversal(root, consumer);
        } else {
            System.out.println("非遞歸後序遍歷: ");
            postOrderTraversal0(root, consumer);
        }
    }

    private void postOrderTraversal(TreeNode root, Consumer<Integer> consumer) {
        if(root != null) {
            postOrderTraversal(root.left, consumer);
            postOrderTraversal(root.right, consumer);
            consumer.accept(root.data);
        }
    }

    private void postOrderTraversal0(TreeNode root, Consumer<Integer> consumer) {
        // ----- | mid | right | left ...
        java.util.LinkedList<TreeNode> sourceStack = new java.util.LinkedList<>();
        // ----- | left | right | mid ...
        java.util.LinkedList<TreeNode> targetStack = new java.util.LinkedList<>();
        sourceStack.push(root);
        while(!sourceStack.isEmpty()) {
            TreeNode node = sourceStack.pop();
            targetStack.push(node);
            if(node.left != null) {
                sourceStack.push(node.left);
            }
            if(node.right != null) {
                sourceStack.push(node.right);
            }
        }
        while(!targetStack.isEmpty()) {
            consumer.accept(targetStack.pop().data);
        }
    }

    private static class TreeNode {
        private static final int NULL = Integer.MIN_VALUE;

        int data;
        TreeNode left;
        TreeNode right;

        TreeNode(int data) {
            this.data = data;
        }
    }

    /**
     * 測試深入優先遍歷的:前序,中序,後序
     *               3
     *            ↙     ↘
     *          2         8
     *       ↙    ↘      ↙  ↘
     *     9       10  NULL   4
     *   ↙  ↘     ↙  ↘
     *NULL NULL  NULL NULL
     *
     * pre  : 3 2 9 10 8 4
     * in   : 9 2 10 3 8 4
     * post : 9 10 2 4 8 3
     * @param args
     */
    public static void main(String[] args) {
        final int NULL = TreeNode.NULL;

        int[] data = { 3, 2, 9, NULL, NULL, 10, NULL, NULL, 8, NULL, 4 };

        LinkedList list = new LinkedList(data);

        BinaryTree binaryTree = BinaryTree.newBinaryTree(list);

        binaryTree.preOrderTraversal(x -> System.out.print(x + " "));
        System.out.println();

        binaryTree.inOrderTraversal(x -> System.out.print(x + " "));
        System.out.println();

        binaryTree.postOrderTraversal(x -> System.out.print(x + " "));
        System.out.println();
    }
}

7. 最小堆

主要是上浮下沉算法的實現,這個要特別關注。

public class MinHeap {
    private int[] data;

    public MinHeap(int[] data) {
        this.data = data;
    }

    /**
     * 構建二叉堆,本質就是讓所有非葉子節點一次"下沉"
     * @param data
     * @return
     */
    public static MinHeap buildHeap(int[] data) {
        Objects.requireNonNull(data);
        MinHeap heap = new MinHeap(data);
        for(int i = (data.length-2)/2; i >= 0 ; i--) {
            heap.downAdjust(i, data.length);
        }
        return heap;
    }

    /**
     *       parentIndex(tmp)
     *         ↙    ↘
     *  childIndex  childIndex+1
     * @param parentIndex 讓parentIndex元素下沉
     */
    public void downAdjust(int parentIndex, int size) {
        // 我們要下沉的元素存起來,等到最終位置確定了,再進行賦值即可
        int tmp = data[parentIndex];
        int childIndex = (parentIndex<<1) + 1;
        while(childIndex < size) {
            if(childIndex+1 < size                          // 如果有右孩子
                && data[childIndex+1] < data[childIndex]) { // 且右孩子小於左孩子
                childIndex++;
            }
            // 最小堆, tmp爲父節點
            if(tmp <= data[childIndex]) {
               break;
            }
            data[parentIndex] = data[childIndex];
            parentIndex = childIndex;
            childIndex = (childIndex<<1) + 1;
        }
        data[parentIndex] = tmp;
    }

    public void upAdjust(int childIndex) {
        // int childIndex = data.length-1;
        int parentIndex = (childIndex-1) >> 1;
        int tmp = data[childIndex];
        while(childIndex > 0 && tmp < data[parentIndex]) {
            data[childIndex] = data[parentIndex];
            childIndex = parentIndex;
            parentIndex = (childIndex-1) >> 1;
        }
        data[childIndex] = tmp;
    }

    public static void main(String[] args) {
        int[] arr = { 1, 3, 2, 6, 5, 7, 8, 9, 10, 0 };
        MinHeap minHeap = new MinHeap(arr);
        minHeap.upAdjust(arr.length-1);
        System.out.println(Arrays.toString(arr));

        arr = new int[]{ 7, 1, 3, 10, 5, 2, 8, 9, 6 };
        minHeap = MinHeap.buildHeap(arr);
        System.out.println(Arrays.toString(arr));
    }
}

8. 最小優先隊列

使用最小堆實現最小優先隊列

public class PriorityQueue {

    private MinHeap heap;

    private int[] data;

    private int size;

    private final static int DEFAULT_SIZE = 32;

    public PriorityQueue() {
        this(DEFAULT_SIZE);
    }

    public PriorityQueue(int capacity) {
        if(capacity <= 0) {
            throw new RuntimeException("Capacity must be > 0");
        }
        data = new int[capacity];
        heap = new MinHeap(data);
    }

    public void enQueue(int key) {
        if(size >= data.length) {
            resize();
        }
        data[size++] = key;
        heap.upAdjust(size-1);
    }

    public int deQueue() {
        if(size <= 0) {
            throw new RuntimeException("Empty Queue...");
        }
        int head = data[0];
        data[0] = data[--size];
        heap.downAdjust(0, size);
        return head;
    }

    private void resize() {
        int newSize = size << 1;
        data = Arrays.copyOf(data, newSize);
        heap = new MinHeap(data);
    }

    public static void main(String[] args) {
        PriorityQueue q = new PriorityQueue();
        int[] arr = { 3, 5, 10, 2, 7 };
        for(int i = 0 ; i < arr.length ; i++) {
            q.enQueue(arr[i]);
        }
        for(int i = 0 ; i < arr.length ; i++) {
            System.out.print(q.deQueue() + " ");
        }
    }
}

9. 排序——比較交換

這裏實現了一些基本的排序算法:

  • 冒泡排序,及其簡單優化
  • 快速排序單路快排雙路快排三路快排,基於棧的非遞歸實現快速排序
  • 堆排序這裏的堆排序用到了上面我實現的堆的數據結構
public class Sort {

    private static Random random = new Random(System.currentTimeMillis());

    /**
     * 基礎冒泡排序
     * @param data
     */
    public static void bubbleSort(int[] data) {
        Objects.requireNonNull(data);
        for(int i = 0 ; i < data.length ; i++) {
            for(int j = 0 ; j < data.length-1-i ; j++) {
                if(data[j] > data[j+1]) {
                    swap(data, j, j+1);
                }
            }
        }
    }

    /**
     * 冒泡排序簡單優化1
     * @param data
     */
    public static void bubbleSort1(int[] data) {
        Objects.requireNonNull(data);
        for(int i = 0 ; i < data.length ; i++) {
            boolean isSorted = true;
            for(int j = 0 ; j < data.length-1-i ; j++) {
                if(data[j] > data[j+1]) {
                    swap(data, j, j+1);
                    isSorted = false;
                }
            }
            if(isSorted) {
                break;
            }
        }
    }

    /**
     * 冒泡排序簡單優化2
     * @param data
     */
    public static void bubbleSort2(int[] data) {
        Objects.requireNonNull(data);
        int lastChangeIndex = 0;
        int border = data.length - 1;
        for(int i = 0 ; i < data.length ; i++) {
            boolean isSorted = true;
            for(int j = 0 ; j < border ; j++) {
                if(data[j] > data[j+1]) {
                    swap(data, j, j+1);
                    isSorted = false;
                    lastChangeIndex = j;
                }
            }
            border = lastChangeIndex;
            if(isSorted) {
                break;
            }
        }
    }

    /**
     * 快速排序入口
     * @param arr
     */
    public static void quickSort(int[] arr) {
        Objects.requireNonNull(arr);
        if (random.nextBoolean()) {
            System.out.println("基於遞歸的快速排序: ");
            quickSort(arr, 0, arr.length - 1);
        } else {
            System.out.println("基於棧的快速排序: ");
            quickSort0(arr, 0, arr.length - 1);
        }
    }

    /**
     * 快速排序入口子方法
     * @param arr
     * @param l
     * @param r
     */
    private static void quickSort(int[] arr, int l, int r) {
        if(l >= r) {
            return;
        }
        // 這裏的3Way可以換成多種不同的實現,如1Way,2Way等
        int pivotIndex = partition3Way(arr, l, r);
        quickSort(arr, l, pivotIndex-1);
        quickSort(arr, pivotIndex+1, r);
    }

    /**
     * 基於棧的快速排序
     * @param arr
     * @param l
     * @param r
     */
    private static void quickSort0(int[] arr, int l, int r) {
        LinkedList<Pair> stack = new LinkedList<>();
        stack.push(new Pair(l, r));
        while(!stack.isEmpty()) {
            Pair top = stack.pop();
            int pivotIndex = partition3Way(arr, top.l, top.r);
            if(top.l + 1 < pivotIndex) {
                stack.push(new Pair(top.l, pivotIndex-1));
            }
            if(pivotIndex + 1 < top.r) {
                stack.push(new Pair(pivotIndex+1, top.r));
            }
        }
    }

    /**
     * arr[l, p-1] < arr[p] and arr[p+1, r] > arr[p]
     *
     * //  arr[l+1, j] < v   ;   arr[j+1, i) > v
     * //  | arr[l] |  \   \   \   \   \   \   \   |   /   /   /  /  /  /  /  /  |
     * //           l+1                          j  j+1                           i
     * @param arr
     * @param l
     * @param r
     * @return p
     */
    private static int partition1Way(int[] arr, int l, int r) {
        int j = l, i = l+1;
        while(i <= r) {
            if(arr[i] < arr[l]) {
                swap(arr, ++j, i);
            }
            i++;
        }
        swap(arr, l, j);
        return j;
    }

    /**
     * arr[l+1, i) < v   ;  arr(j, r] > v
     * @param arr
     * @param l
     * @param r
     * @return
     */
    private static int partition2Way(int[] arr, int l, int r) {
        int pivot = arr[l];
        int i = l+1, j = r;
        while(true) {
            while(i <= r && arr[i] < pivot) {
                i++;
            }
            while(j >= l+1 && arr[j] > pivot) {
                j--;
            }
            if(i > j) {
                break;
            }
            swap(arr, i++, j--);
        }
        // i爲從前向後看第一個 >= v的元素索引
        // j爲從後向前看第一個 <= v的元素索引
        swap(arr, l, j);
        return j;
    }

    /**
     * arr[l+1, lt] < v  ;   arr[lt+1, i) == v   ;  arr[gt, r] > v
     *
     * [l] [l+1 ... lt] [lt+1 ... i-1] [i .... ] [gt ... r]
     *         < v          == v                     > v
     * i == gt -> break;
     * @param arr
     * @param l
     * @param r
     * @return
     */
    private static int partition3Way(int[] arr, int l, int r) {
        int pivot = arr[l];
        int lt = l, i = l+1, gt = r+1;
        while(i < gt) {
            if(arr[i] < pivot) {
                swap(arr, ++lt, i);
            } else if(arr[i] > pivot){
                swap(arr, i--, --gt);
            }
            i++;
        }
        swap(arr, l, lt);
        return lt;
    }


    private static class Pair {
        int l, r;

        public Pair(int l, int r) {
            this.l = l;
            this.r = r;
        }
    }

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

    /**
     * 以最小堆排序,能夠得到降序(從大到小)的數組序列
     * @param arr
     */
    public static void heapSort(int[] arr) {
        Objects.requireNonNull(arr);
        MinHeap heap = MinHeap.buildHeap(arr);
        for(int i = arr.length-1 ; i > 0 ; i--) {
            swap(arr, 0, i);
            heap.downAdjust(0, i);
        }
    }

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

10. 排序——線性時間排序

這裏實現了計數排序桶排序

public class LinearSort {

    /**
     * 這裏我們假定元素的取值範圍爲0~10
     * @param arr
     */
    public static void counterSort(int[] arr) {
        int[] counter = new int[11];
        for(int i = 0 ; i < arr.length ; i++) {
            counter[arr[i]]++;
        }

        int k = 0;
        for(int i = 0 ; i < counter.length ; i++) {
            int count = counter[i];
            for(int j = 0 ; j < count ; j++) {
                arr[k++] = i;
            }
        }
    }

    /**
     * 假定元素的範圍全部在[min, max]之間,且這個區間比較小
     * 我們使用如下的計數排序來優化
     * @param arr
     */
    public static void counterSort1(int[] arr) {
        Objects.requireNonNull(arr);

        // 首先找到最小值,最大值
        int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
        for(int i = 0 ; i < arr.length ; i++) {
            min = Math.min(min, arr[i]);
            max = Math.max(max, arr[i]);
        }

        // 遍歷,並開始計數
        int[] counter = new int[max - min + 1];
        for(int i = 0 ; i < arr.length ; i++) {
            counter[arr[i] - min]++;
        }

        // 寫回數據,相當於對原數組排序
        int k = 0;
        for(int i = 0 ; i < counter.length ; i++) {
            int count = counter[i];
            for(int j = 0 ; j < count ; j++) {
                // 注意,這裏i加上了一個偏移量
                arr[k++] = i+min;
            }
        }
    }

    /**
     * 計數排序要求每個元素都是整數,因爲數組的索引必須爲整數
     * 而桶排序則解決了這種困難
     * @param arr
     */
    public static void bucketSort(double[] arr) {
        Objects.requireNonNull(arr);
        double min = Double.MAX_VALUE, max = Double.MIN_VALUE;
        for(int i = 0 ; i < arr.length ; i++) {
            min = Math.min(min, arr[i]);
            max = Math.max(max, arr[i]);
        }

        double d = max - min;
        int bucketNum = arr.length;
        ArrayList<LinkedList<Double>> bucketList = new ArrayList<>(bucketNum);
        for(int i = 0 ; i < bucketNum ; i++) {
            bucketList.add(new LinkedList<>());
        }

        for(int i = 0 ; i < arr.length ; i++) {
            int num = (int)((arr[i] - min) * (bucketNum-1) / d);
            bucketList.get(num).add(arr[i]);
        }

        for(LinkedList<Double> list : bucketList) {
            Collections.sort(list);
        }

        int index = 0;
        for(LinkedList<Double> list : bucketList) {
            for(double data : list) {
                arr[index++] = data;
            }
        }
    }

    public static void main(String[] args) {
        //int[] arr;
        //counterSort(new int[]{ 4, 4, 6, 5, 3, 2, 8, 1, 7, 5, 6, 0, 10 });
        //counterSort1(arr = new int[]{ 95, 94, 91, 98, 99, 90, 93, 91, 92 });
        double[] arr = { 4.12, 6.421, 0.0023, 3.0, 2.123, 8.122, 4.12, 10.09 };
        bucketSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章