雙路快速排序演算法及三路快速排序演算法視覺化
阿新 • • 發佈:2018-12-25
雙路快速排序演算法
工具類
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.lang.InterruptedException;
public class AlgoVisHelper {
private AlgoVisHelper(){}
public static final Color Red = new Color(0xF44336);
public static final Color Pink = new Color(0xE91E63);
public static final Color Purple = new Color(0x9C27B0);
public static final Color DeepPurple = new Color(0x673AB7);
public static final Color Indigo = new Color(0x3F51B5);
public static final Color Blue = new Color(0x2196F3);
public static final Color LightBlue = new Color(0x03A9F4 );
public static final Color Cyan = new Color(0x00BCD4);
public static final Color Teal = new Color(0x009688);
public static final Color Green = new Color(0x4CAF50);
public static final Color LightGreen = new Color(0x8BC34A);
public static final Color Lime = new Color(0xCDDC39);
public static final Color Yellow = new Color(0xFFEB3B);
public static final Color Amber = new Color(0xFFC107);
public static final Color Orange = new Color(0xFF9800);
public static final Color DeepOrange = new Color(0xFF5722);
public static final Color Brown = new Color(0x795548);
public static final Color Grey = new Color(0x9E9E9E);
public static final Color BlueGrey = new Color(0x607D8B);
public static final Color Black = new Color(0x000000);
public static final Color White = new Color(0xFFFFFF);
public static void strokeCircle(Graphics2D g, int x, int y, int r){
Ellipse2D circle = new Ellipse2D.Double(x-r, y-r, 2*r, 2*r);
g.draw(circle);
}
public static void fillCircle(Graphics2D g, int x, int y, int r){
Ellipse2D circle = new Ellipse2D.Double(x-r, y-r, 2*r, 2*r);
g.fill(circle);
}
public static void strokeRectangle(Graphics2D g, int x, int y, int w, int h){
Rectangle2D rectangle = new Rectangle2D.Double(x, y, w, h);
g.draw(rectangle);
}
public static void fillRectangle(Graphics2D g, int x, int y, int w, int h){
Rectangle2D rectangle = new Rectangle2D.Double(x, y, w, h);
g.fill(rectangle);
}
public static void setColor(Graphics2D g, Color color){
g.setColor(color);
}
public static void setStrokeWidth(Graphics2D g, int w){
int strokeWidth = w;
g.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
}
public static void pause(int t) {
try {
Thread.sleep(t);
}
catch (InterruptedException e) {
System.out.println("Error sleeping");
}
}
}
檢視層
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class AlgoFrame extends JFrame{
private int canvasWidth;
private int canvasHeight;
public AlgoFrame(String title, int canvasWidth, int canvasHeight){
super(title);
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
AlgoCanvas canvas = new AlgoCanvas();
setContentPane(canvas);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setVisible(true);
}
public AlgoFrame(String title){
this(title, 1024, 768);
}
public int getCanvasWidth(){return canvasWidth;}
public int getCanvasHeight(){return canvasHeight;}
// data
private TwoWaysQuickSortData data;
public void render(TwoWaysQuickSortData data){
this.data = data;
repaint();
}
private class AlgoCanvas extends JPanel{
public AlgoCanvas(){
// 雙快取
super(true);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
// 抗鋸齒
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.addRenderingHints(hints);
// 具體繪製
int w = canvasWidth/data.N();
for(int i = 0 ; i < data.N() ; i ++ ) {
//是否在處理區間中
if ( i >= data.l && i <= data.r)
AlgoVisHelper.setColor(g2d, AlgoVisHelper.Green);
else
AlgoVisHelper.setColor(g2d, AlgoVisHelper.Grey);
//是否是標定點
if( i == data.curPivot )
AlgoVisHelper.setColor(g2d, AlgoVisHelper.Indigo);
//是否以排序
if( i >= data.l + 1 && i <= data.curL)
AlgoVisHelper.setColor(g2d, AlgoVisHelper.LightBlue);
//是否以排序
if( i >= data.curR && i <= data.r)
AlgoVisHelper.setColor(g2d, AlgoVisHelper.LightBlue);
//是否以處理
if( data.fixedPivots[i] )
AlgoVisHelper.setColor(g2d, AlgoVisHelper.Red);
AlgoVisHelper.fillRectangle(g2d, i * w, canvasHeight - data.get(i), w - 1, data.get(i));
}
}
@Override
public Dimension getPreferredSize(){
return new Dimension(canvasWidth, canvasHeight);
}
}
}
資料層
import java.util.Arrays;
public class TwoWaysQuickSortData {
public enum Type{
Default,
NearlyOrdered,
Identical
}
private int[] numbers;
public int l, r;
public boolean[] fixedPivots;
public int curPivot;
public int curL, curR;
public TwoWaysQuickSortData(int N, int randomBound, Type dataType){
numbers = new int[N];
fixedPivots = new boolean[N];
int lBound = 1;
int rBound = randomBound;
if(dataType == Type.Identical){
int avgNumber = (lBound + rBound) / 2;
lBound = avgNumber;
rBound = avgNumber;
}
for( int i = 0 ; i < N ; i ++) {
numbers[i] = (int)(Math.random()*(rBound-lBound+1)) + lBound;
fixedPivots[i] = false;
}
if(dataType == Type.NearlyOrdered){
Arrays.sort(numbers);
int swapTime = (int)(0.01*N);
for(int i = 0 ; i < swapTime; i ++){
int a = (int)(Math.random()*N);
int b = (int)(Math.random()*N);
swap(a, b);
}
}
}
public TwoWaysQuickSortData(int N, int randomBound){
this(N, randomBound, Type.Default);
}
public int N(){
return numbers.length;
}
public int get(int index){
if( index < 0 || index >= numbers.length)
throw new IllegalArgumentException("Invalid index to access Sort Data.");
return numbers[index];
}
public void swap(int i, int j) {
if( i < 0 || i >= numbers.length || j < 0 || j >= numbers.length)
throw new IllegalArgumentException("Invalid index to access Sort Data.");
int t = numbers[i];
numbers[i] = numbers[j];
numbers[j] = t;
}
}
控制層
import java.awt.EventQueue;
public class AlgoVisualizer {
private static int DELAY = 20;
private TwoWaysQuickSortData data;
private AlgoFrame frame;
public AlgoVisualizer(int sceneWidth, int sceneHeight, int N, TwoWaysQuickSortData.Type dataType){
// 初始化資料
data = new TwoWaysQuickSortData(N, sceneHeight, dataType);
// 初始化檢視
EventQueue.invokeLater(() -> {
frame = new AlgoFrame("Two Ways Quick Sort Visualization", sceneWidth, sceneHeight);
new Thread(() -> {
run();
}).start();
});
}
public AlgoVisualizer(int sceneWidth, int sceneHeight, int N){
this(sceneWidth, sceneHeight, N, TwoWaysQuickSortData.Type.Default);
}
public void run(){
//初始化渲染
setData(-1, -1, -1, -1, -1, -1);
quickSort2Ways(0, data.N()-1);
//最終渲染
setData(-1, -1, -1, -1, -1, -1);
}
private void quickSort2Ways(int l, int r){
if( l > r )
return;
if( l == r ) {
setData(l, r, l, -1, -1, -1);
return;
}
//渲染處理區間
setData(l, r, -1, -1, -1, -1);
int p = partition(l, r);
quickSort2Ways(l, p-1 );
quickSort2Ways(p+1, r);
}
private int partition(int l, int r){
//隨機獲取區間一個標定點
int p = (int)(Math.random()*(r-l+1)) + l;
//渲染所處理區間及標定點
setData(l, r, -1, p, -1, -1);
//起始點與標定點交換
data.swap(l, p);
int v = data.get(l);
setData(l, r, -1, l, -1, -1);
// arr[l+1...i) <= v; arr(j...r] >= v
int i = l+1, j = r;
setData(l, r, -1, l, i, j);
while( true ){
//當前處理資料是否小於標定值,小於移動
while( i <= r && data.get(i) < v ){
i ++;
setData(l, r, -1, l, i, j);
}
//當前處理資料是否小大於標定值,大於移動
while( j >= l+1 && data.get(j) > v ){
j --;
setData(l, r, -1, l, i, j);
}
if( i > j )
break;
//如果上面不成立則交換2個標定值的位置
data.swap( i, j );
i ++;
j --;
setData(l, r, -1, l, i, j);
}
data.swap(l, j);
setData(l, r, j, -1, -1, -1);
return j;
}
private void setData(int l, int r, int fixedPivot, int curPivot, int curL, int curR){
data.l = l;
data.r = r;
if(fixedPivot != -1)
data.fixedPivots[fixedPivot] = true;
data.curPivot = curPivot;
data.curL = curL;
data.curR = curR;
frame.render(data);
AlgoVisHelper.pause(DELAY);
}
public static void main(String[] args) {
int sceneWidth = 800;
int sceneHeight = 800;
int N = 100;
AlgoVisualizer vis = new AlgoVisualizer(sceneWidth, sceneHeight, N, TwoWaysQuickSortData.Type.Default);
// AlgoVisualizer vis = new AlgoVisualizer(sceneWidth, sceneHeight, N, TwoWaysQuickSortData.Type.NearlyOrdered);
// AlgoVisualizer vis = new AlgoVisualizer(sceneWidth, sceneHeight, N, TwoWaysQuickSortData.Type.Identical);
}
}
執行效果圖
雙路快速排序演算法避免了在資料幾乎相同的情況下退化成O(n^2)級別的演算法
三路快速排序演算法
三路快速排序演算法在大部分值相等的情況下可進化成O(n)級別的演算法
注:資料層和工具類上例有、不重複了
檢視層
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class AlgoFrame extends JFrame{
private int canvasWidth;
private int canvasHeight;
public AlgoFrame(String title, int canvasWidth, int canvasHeight){
super(title);
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
AlgoCanvas canvas = new AlgoCanvas();
setContentPane(canvas);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setVisible(true);
}
public AlgoFrame(String title){
this(title, 1024, 768);
}
public int getCanvasWidth(){return canvasWidth;}
public int getCanvasHeight(){return canvasHeight;}
// data
private ThreeWaysQuickSortData data;
public void render(ThreeWaysQuickSortData data){
this.data = data;
repaint();
}
private class AlgoCanvas extends JPanel{
public AlgoCanvas(){
// 雙快取
super(true);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
// 抗鋸齒
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.addRenderingHints(hints);
// 具體繪製
int w = canvasWidth/data.N();
for(int i = 0 ; i < data.N() ; i ++ ) {
// 判斷當前值是否存在處理區間中
if ( i >= data.l && i <= data.r)
AlgoVisHelper.setColor(g2d, AlgoVisHelper.Green);
else
AlgoVisHelper.setColor(g2d, AlgoVisHelper.Grey);
// 判斷當前值是否是標定值
if( i == data.curPivot )
AlgoVisHelper.setColor(g2d, AlgoVisHelper.Indigo);
// 是否在左區間中
if( i >= data.l + 1 && i <= data.curL)
AlgoVisHelper.setColor(g2d, AlgoVisHelper.LightBlue);
// 是否在右區間中
if( i >= data.curR && i <= data.r)
AlgoVisHelper.setColor(g2d, AlgoVisHelper.LightBlue);
//是否以排序
if( data.fixedPivots[i] )
AlgoVisHelper.setColor(g2d, AlgoVisHelper.Red);
AlgoVisHelper.fillRectangle(g2d, i * w, canvasHeight - data.get(i), w - 1, data.get(i));
}
}
@Override
public Dimension getPreferredSize(){
return new Dimension(canvasWidth, canvasHeight);
}
}
}
控制層
import java.awt.EventQueue;
public class AlgoVisualizer {
private static int DELAY = 20;
private ThreeWaysQuickSortData data;
private AlgoFrame frame;
public AlgoVisualizer(int sceneWidth, int sceneHeight, int N, ThreeWaysQuickSortData.Type dataType){
// 初始化資料
data = new ThreeWaysQuickSortData(N, sceneHeight, dataType);
// 初始化檢視
EventQueue.invokeLater(() -> {
frame = new AlgoFrame("Three Ways Quick Sort Visualization", sceneWidth, sceneHeight);
new Thread(() -> {
run();
}).start();
});
}
public AlgoVisualizer(int sceneWidth, int sceneHeight, int N){
this(sceneWidth, sceneHeight, N, ThreeWaysQuickSortData.Type.Default);
}
public void run(){
//初始化渲染檢視
setData(-1, -1, -1, -1, -1, -1);
quickSort3Ways(0, data.N()-1);
//最終檢視渲染
setData(-1, -1, -1, -1, -1, -1);
}
private void quickSort3Ways(int l, int r){
if( l > r )
return;
if( l == r ) {
setData(l, r, l, -1, -1, -1);
return;
}
//渲染以處理區間
setData(l, r, -1, -1, -1, -1);
// 隨機在arr[l...r]的範圍中, 選擇一個數值作為標定點pivot
int p = (int)(Math.random()*(r-l+1)) + l;
//渲染以處理區間及標定點
setData(l, r, -1, p, -1, -1);
data.swap(l, p);
int v = data.get(l);
//渲染以處理區間
setData(l, r, -1, l, -1, -1);
int lt = l; // arr[l+1...lt] < v
int gt = r + 1; // arr[gt...r] > v
int i = l+1; // arr[lt+1...i) == v
setData(l, r, -1, l, lt, gt);
while( i < gt ){
//如果小於標定值則把當前值與等於當前值的區間第一個進行交換,i++
if( data.get(i) < v ){
data.swap( i, lt+1);
i ++;
lt ++;
}
//當前值如果大於則與大於標定值區間第一位的前一位交換
else if( data.get(i) > v ){
data.swap( i, gt-1);
gt --;
}
//否則等於++進行處理
else // arr[i] == v
i ++;
setData(l, r, -1, l, i, gt);
}
data.swap( l, lt );
//渲染最終處理的3個區間
setData(l, r, lt, -1, -1, -1);
quickSort3Ways(l, lt-1 );
quickSort3Ways(gt, r);
}
private void setData(int l, int r, int fixedPivot, int curPivot, int curL, int curR){
data.l = l;
data.r = r;
if(fixedPivot != -1){
data.fixedPivots[fixedPivot] = true;
int i = fixedPivot;
while(i < data.N() && data.get(i) == data.get(fixedPivot)){
data.fixedPivots[i] = true;
i ++;
}
}
data.curPivot = curPivot;
data.curL = curL;
data.curR = curR;
frame.render(data);
AlgoVisHelper.pause(DELAY);
}
public static void main(String[] args) {
int sceneWidth = 800;
int sceneHeight = 800;
int N = 100;
// AlgoVisualizer vis = new AlgoVisualizer(sceneWidth, sceneHeight, N, ThreeWaysQuickSortData.Type.Default);
// AlgoVisualizer vis = new AlgoVisualizer(sceneWidth, sceneHeight, N, ThreeWaysQuickSortData.Type.NearlyOrdered);
AlgoVisualizer vis = new AlgoVisualizer(sceneWidth, sceneHeight, N, ThreeWaysQuickSortData.Type.Identical);
}
}