算法4-归并、快速及优先队列

大家好,这里记录下归并排序、快速排序、优先队列的练习过程。

本博客代码示例均来自:算法 Algorithmes Forth Edition
[美] Robert Sedgewick Kevin Wayne 著 谢路云译

一、归并排序

1) 自顶向下的归并排序

package com.chm.algorithms;

import org.junit.jupiter.api.Test;

import static com.chm.algorithms.Example.merge;

/**
 * Author:meice Huang
 * Time: 2020/3/21 下午8:58
 * <p>
 * 自顶向下的归并排序—— 高效算法设计中分治思想的最典型例子
 */
public class Merge {

    private static Comparable[] aux; //归并所需要的辅助数组

    public static void sort(Comparable[] a) {
        aux = new Comparable[a.length];
        sort(a, 0, a.length - 1);
    }

    private static void sort(Comparable[] a, int lo, int hi) {
        //将数组a[lo..hi]排序
        if (hi <= lo) return;
        int mid = lo + (hi - lo) / 2;
        sort(a, lo, mid);// 将左半边排序
        sort(a, mid + 1, hi);//将右半边排序
        merge(a, lo, mid, hi);
    }

    @Test
    public void testMergeSort() {
        sort(new Comparable[]{10, 3, 7, 8, 5, 1, 4, 9, 2, 6});
    }
}


2)自底向上的归并排序

package com.chm.algorithms;

/**
 * Author:meice Huang
 * Time: 2020/3/21 下午10:18
 * <p>
 * 基于堆的优先队列
 */
public class MaxPQ<Key extends Comparable<Key>> {

    private Key[] pq;//基于堆的完全二叉树
    private int N = 0; //存储与pq[1 .. N ]中,pq[0]没有使用

    public MaxPQ(int maxN) {
        pq = (Key[]) new Comparable[maxN + 1];
    }

    public boolean isEmpty() {
        return N == 0;
    }

    public int size() {
        return N;
    }

    public void insert(Key v) {
        pq[++N] = v;
        swim(N);
    }

    public Key delMax() {
        Key max = pq[1];//从根节点得到最大元素
        exch(1, N--);//和最后一个节点交换
        pq[N + 1] = null;//防止对象游离
        sink(1);//恢复堆的有序性
        return max;
    }

    private boolean less(int i, int j) {
        return pq[i].compareTo(pq[j]) < 0;
    }

    private void exch(int i, int j) {
        Key t = pq[i];
        pq[i] = pq[j];
        pq[j] = t;
    }

    private void swim(int k) {
        while (k > 1 && less(k / 2, k)) {
            exch(k / 2, k);
            k = k / 1;
        }
    }

    private void sink(int k) {
        while (2 * k <= N) {
            int j = 2 * k;
            if (j < N && less(j, j + 1)) {
                j++;
            }
            if (!less(k, j)) {
                break;
            }
            exch(k, j);
            k = j;
        }
    }

    /**
     * 堆排序
     */

    public void sort(Comparable[] a) {
        int N = a.length;
        for (int k = N / 2; k >= 1; k--) {
//            sink(a,k,N);
            while (N > 1) {
                Example.exch(a, 1, N--);
//              sink(a,1,N);

            }
        }
    }

}


二、快速排序

1) 基本快速排序

package com.chm.algorithms;

import edu.princeton.cs.algs4.StdRandom;

import static com.chm.algorithms.Example.exch;
import static com.chm.algorithms.Example.less;

/**
 * Author:meice Huang
 * Time: 2020/3/21 下午9:28
 * <p>
 * 快速排序
 * 快速排序也是分治的思想,把一个数组分成2个子数组,将两部分独立的排序,子数组有序后整个数组自然就有序了。
 * 归并排序子数组有序后还需要再次归并成有序的。
 */
public class Quick {

    public static void sort(Comparable[] a) {
        StdRandom.shuffle(a);//消除对输入的依赖
        sort(a, 0, a.length - 1); // 这里调用可以换成三向切分的私有sort()方法(Quick3way)

    }

    private static void sort(Comparable[] a, int lo, int hi) {
        if (hi <= lo) return;
        int j = partition(a, lo, hi);//切分
        sort(a, lo, j - 1);//将左半部分a[lo .. j-1]排序
        sort(a, j + 1, hi);//将右半部分a[j+1 .. hi]排序
    }

    private static int partition(Comparable[] a, int lo, int hi) {
        //将素组切分为a[lo.. i-1], a[i],a[i+1..hi]
        int i = lo, j = hi + 1;//左右扫描指针
        Comparable v = a[lo];//切分元素
        while (true) {
            //扫描左右,检查扫描是否结束并交换元素
            while (less(a[++i], v)) {
                if (i == hi) {
                    break;
                }
            }
            while (less(v, a[--j])) {
                if (j == lo) {
                    break;
                }
            }
            if (i >= j) break;
            exch(a, i, j);
        }
        exch(a, lo, j);//将v = a[j]放入正确的位置
        return j;//a[lo..j-1] <= a[j] <= a[j+1..hi] Bingo!
    }
}

2)三向切分快排

package com.chm.algorithms;

import static com.chm.algorithms.Example.exch;

/**
 * Author:meice Huang
 * Time: 2020/3/21 下午9:46
 * 三向切分的快速排序
 * 荷兰国旗问题
 */
public class Quick3way {
    private static void sort(Comparable[] a, int lo, int hi) {
        //调用此私有方法,可通过Quick类的共有方法调用
        if (hi <= lo) return;
        int lt = lo, i = lo + 1, gt = hi;
        Comparable v = a[lo];
        while (i <= gt) {
            int cmp = a[i].compareTo(v);
            if (cmp < 0) {
                exch(a, lt++, i++);
            } else if (cmp > 0) {
                exch(a, i, gt--);
            } else {
                i++;
            }
        }//现在 a[lo .. lt-1] < v =a[lt .. gt] < a[gt+1 .. hi]成立
        sort(a, lo, lt - 1);
        sort(a, gt + 1, hi);
    }
}


三、优先队列(堆实现)

1、一个优先队列的用例

package com.chm.algorithms;

import edu.princeton.cs.algs4.MinPQ;
import edu.princeton.cs.algs4.Stack;
import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.Transaction;

/**
 * Author:meice Huang
 * Time: 2020/3/21 下午10:09
 * 优先队列
 */
public class TopM {
    public static void main(String[] args) {
        //打印输入流中最大的M行
        int M = Integer.parseInt(args[0]);
        MinPQ<Transaction> pq = new MinPQ<Transaction>(M + 1);
        while (StdIn.hasNextLine()) {
            //为下一行输入创建一个元素并放入优先队列中
            pq.insert(new Transaction(StdIn.readLine()));
            if (pq.size() > M) {
                pq.delMin();//如果优先队列中存在M+1个元素则删除其中最小的元素
            }
        }//最大的M个元素都在优先队列中
        Stack<Transaction> stack = new Stack<>();
        while (!pq.isEmpty()) {
            stack.push(pq.delMin());
        }
        for (Transaction t : stack) {
            StdOut.println(t);
        }
    }
}

2、基于堆的优先队列

package com.chm.algorithms;

/**
 * Author:meice Huang
 * Time: 2020/3/21 下午10:18
 * <p>
 * 基于堆的优先队列
 */
public class MaxPQ<Key extends Comparable<Key>> {

    private Key[] pq;//基于堆的完全二叉树
    private int N = 0; //存储与pq[1 .. N ]中,pq[0]没有使用

    public MaxPQ(int maxN) {
        pq = (Key[]) new Comparable[maxN + 1];
    }

    public boolean isEmpty() {
        return N == 0;
    }

    public int size() {
        return N;
    }

    public void insert(Key v) {
        pq[++N] = v;
        swim(N);
    }

    public Key delMax() {
        Key max = pq[1];//从根节点得到最大元素
        exch(1, N--);//和最后一个节点交换
        pq[N + 1] = null;//防止对象游离
        sink(1);//恢复堆的有序性
        return max;
    }

    private boolean less(int i, int j) {
        return pq[i].compareTo(pq[j]) < 0;
    }

    private void exch(int i, int j) {
        Key t = pq[i];
        pq[i] = pq[j];
        pq[j] = t;
    }

    private void swim(int k) {
        while (k > 1 && less(k / 2, k)) {
            exch(k / 2, k);
            k = k / 1;
        }
    }

    private void sink(int k) {
        while (2 * k <= N) {
            int j = 2 * k;
            if (j < N && less(j, j + 1)) {
                j++;
            }
            if (!less(k, j)) {
                break;
            }
            exch(k, j);
            k = j;
        }
    }

    /**
     * 堆排序
     */

    public void sort(Comparable[] a) {
        int N = a.length;
        for (int k = N / 2; k >= 1; k--) {
//            sink(a,k,N);
            while (N > 1) {
                Example.exch(a, 1, N--);
//              sink(a,1,N);

            }
        }
    }

}

3、使用优先队列的多项归并

package com.chm.algorithms;

import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.IndexMinPQ;
import edu.princeton.cs.algs4.StdOut;

/**
 * Author:meice Huang
 * Time: 2020/3/21 下午10:32
 * 使用优先队列的多项合并
 */
public class Multiway {
    public static void merge(In[] streams) {
        int N = streams.length;
        IndexMinPQ<String> pq = new IndexMinPQ<>(N);
        for (int i = 0; i < N; i++) {
            if (!streams[i].isEmpty()) {
                pq.insert(i, streams[i].readString());
            }
        }
        while (!pq.isEmpty()) {
            StdOut.println(pq.minKey());
            int i = pq.delMin();
            if (!streams[i].isEmpty()) {
                pq.insert(i, streams[i].readString());
            }
        }
    }

    public static void main(String[] args) {
        int N = args.length;
        In[] streams = new In[N];
        for (int i = 0; i < N; i++) {
            streams[i] = new In(args[i]);
            merge(streams);
        }
    }
}


挺难理解的,你们呢?

发布了181 篇原创文章 · 获赞 66 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/meiceatcsdn/article/details/105019473