From 2f137682ce82af69f6d2f9543d4d33ccf3bdf9e2 Mon Sep 17 00:00:00 2001 From: zejian Date: Sun, 11 Feb 2018 17:51:44 +0800 Subject: [PATCH 1/6] =?UTF-8?q?Dijkstra=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Graph/DirectedWeightGraph/Dijkstra.java | 169 ++++++++++++++++++ .../Graph/WeightGraph/KruskalMST.java | 4 +- .../Graph/WeightGraph/LazyPrimMST.java | 2 +- .../Graph/WeightGraph/MainTest.java | 126 +++++++++++++ .../structures/Graph/WeightGraph/PrimMST.java | 1 - .../Graph/WeightGraph/WeightSparseGraph.java | 1 - testDirectedWeightG1.txt | 9 + 7 files changed, 307 insertions(+), 5 deletions(-) create mode 100644 src/com/zejian/structures/Graph/DirectedWeightGraph/Dijkstra.java create mode 100644 src/com/zejian/structures/Graph/WeightGraph/MainTest.java create mode 100644 testDirectedWeightG1.txt diff --git a/src/com/zejian/structures/Graph/DirectedWeightGraph/Dijkstra.java b/src/com/zejian/structures/Graph/DirectedWeightGraph/Dijkstra.java new file mode 100644 index 0000000..93b44a8 --- /dev/null +++ b/src/com/zejian/structures/Graph/DirectedWeightGraph/Dijkstra.java @@ -0,0 +1,169 @@ +package com.zejian.structures.Graph.DirectedWeightGraph; + +import com.zejian.structures.Graph.WeightGraph.Edge; +import com.zejian.structures.Graph.WeightGraph.WeightGraph; +import com.zejian.structures.Graph.WeightGraph.WeightSparseGraph; +import com.zejian.structures.heap.IndexMinPQ; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * Created by zejian on 2018/2/10. + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] + * Dijkstra算法(迪杰斯特拉算法),解决无负权边的单源最短路径问题,利用广度优先搜索算法 + * 算法思想: + * Dijkstra算法采用的是一种贪心的策略,声明数组 distTo 和 最小索引堆IndexMinPQ , + * 其中distTo 记录s到每个顶点的最短距离,初始时distTo数组中源点 s 的路径权重为0(distTo[s] = 0) + * 若对于顶点 s 存在能直接到达的边(s,m),则把distTo[m]设为w(s, m),同时把所有其他(s不能直接到达的) + * 顶点的路径权重设置为-1。 + * 利用最小索引堆IndexMinPQ对边的权重进行排序,每次获取某个顶点关联最小权重的边 + * 并将当前顶点标记位已被访问,说明源点到该顶点的最短距离已找到.同时搜索该顶点的邻边,将其 + * 对应边的权重加入索引堆中以便下轮循环使用,此时需要对顶点进行一次松弛操作更新distTo数组对应值. + * 下次循环中再次从最小索引堆中获取权重最小的边,依次进行上述操作. + * + */ +public class Dijkstra> { + + private WeightGraph G; + private int s; //源点 + private Number distTo[];//记录s到每个顶点的最短距离 + private Edge edgeTo[]; //edgeTo[i]记录最短路径中, 到达i点的边是哪一条 可以用来恢复整个最短路径 + private boolean marked[]; //标记已被访问的顶点 + + public Dijkstra(WeightSparseGraph G , int s){ + assert G != null; + assert s >= 0; + this.G = G; + this.s = s; + + distTo = new Number[G.V()]; + edgeTo = new Edge[G.V()]; + marked = new boolean[G.V()]; + + for (int i = 0; i pq = new IndexMinPQ(G.V()); + + distTo[s] = 0.0; + Edge es = new Edge(s,s,(Weight) (Number) 0.0); + edgeTo[s] = es; + pq.insert(s,es.wt()); + while (!pq.isEmpty()){ + int v = pq.deleteMin(); + marked[v] = true; + + //遍历邻边 + for (Object o: G.adj(v)) { + Edge e = (Edge) o; + + int w = e.other(v); + //如果邻边的顶点最短路径还没有找到,即未被访问 + if(!marked[w]){ + // 如果w点以前没有访问过, + // 或者访问过, 但是通过当前的v点到w点距离更短, 则进行更新 + if(edgeTo[w] == null || distTo[v].doubleValue() + e.wt().doubleValue() < distTo[w].doubleValue()){ + distTo[w] = distTo[v].doubleValue() + e.wt().doubleValue(); + edgeTo[w] = e; + //更新 + if(pq.contains(w)){ + pq.change(w,e.wt()); + }else { + pq.insert(w,e.wt()); + } + } + + } + } + } + } + + /** + * 获取s到w顶点的最短路径距离 + * @param w + * @return + */ + public Number getShortestPathTo(int w){ + assert w >= 0 && w < G.V(); + assert hasPathTo(w); + return distTo[w]; + } + + /** + * 从s到w是否可达 + * @param w + * @return + */ + public boolean hasPathTo(int w){ + assert w >= 0 && w < G.V(); + return marked[w]; + } + + + /** + * 寻找从s到w的最短路径, 将整个路径经过的边存放在vec中 + * @return + */ + private List> getShortestPath(int w){ + assert w >= 0 && w < G.V(); + assert hasPathTo(w); + + Stack> path = new Stack<>(); + + Edge e = edgeTo[w]; + + while (e.v() != this.s){ + path.push(e); + e = edgeTo[e.v()]; + } + + path.push(e); + + List> pathList = new ArrayList<>(); + + while (!path.empty()){ + pathList.add(path.pop()); + } + + return pathList; + } + + /** + * 打印从s到w顶点的最短路径轨迹 + * @param w + */ + public void showShortestPath(int w){ + + assert w >= 0 && w < G.V(); + assert hasPathTo(w); + + List> pathList = getShortestPath(w); + + for (int i = 0; i < pathList.size() ; i++) { + System.out.print( pathList.get(i).v() + " -> "); + if( i == pathList.size()-1 ) + System.out.println(pathList.get(i).w()); + } + } + + // 测试 + public static void main(String[] args) { + String filename = "testDirectedWeightG1.txt"; + WeightSparseGraph wSparseGraph = new WeightSparseGraph(5,false); + wSparseGraph.readGraph(filename); + wSparseGraph.show(); + + Dijkstra d = new Dijkstra<>(wSparseGraph,0); + + for (int i = 0; i g1 = new WeightSparseGraph(V1, false); + g1.readGraph(filename1); + System.out.println( filename1 + " load successfully."); + + WeightSparseGraph g2 = new WeightSparseGraph(V2, false); + g2.readGraph(filename2); + System.out.println( filename2 + " load successfully."); + + WeightSparseGraph g3 = new WeightSparseGraph(V3, false); + g3.readGraph(filename3); + System.out.println( filename3 + " load successfully."); + + WeightSparseGraph g4 = new WeightSparseGraph(V4, false); + g4.readGraph(filename4); + System.out.println( filename4 + " load successfully."); + + System.out.println(); + + + long startTime, endTime; + + // Test Lazy Prim MST + System.out.println("Test Lazy Prim MST:"); + + startTime = System.currentTimeMillis(); + LazyPrimMST lazyPrimMST1 = new LazyPrimMST(g1); + endTime = System.currentTimeMillis(); + System.out.println("Test for G1: " + (endTime-startTime) + "ms."); + + startTime = System.currentTimeMillis(); + LazyPrimMST lazyPrimMST2 = new LazyPrimMST(g2); + endTime = System.currentTimeMillis(); + System.out.println("Test for G2: " + (endTime-startTime) + "ms."); + + startTime = System.currentTimeMillis(); + LazyPrimMST lazyPrimMST3 = new LazyPrimMST(g3); + endTime = System.currentTimeMillis(); + System.out.println("Test for G3: " + (endTime-startTime) + "ms."); + + startTime = System.currentTimeMillis(); + LazyPrimMST lazyPrimMST4 = new LazyPrimMST(g4); + endTime = System.currentTimeMillis(); + System.out.println("Test for G4: " + (endTime-startTime) + "ms."); + + System.out.println(); + + + // Test Prim MST + System.out.println("Test Prim MST:"); + + startTime = System.currentTimeMillis(); + PrimMST primMST1 = new PrimMST(g1); + endTime = System.currentTimeMillis(); + System.out.println("Test for G1: " + (endTime-startTime) + "ms."); + + startTime = System.currentTimeMillis(); + PrimMST primMST2 = new PrimMST(g2); + endTime = System.currentTimeMillis(); + System.out.println("Test for G2: " + (endTime-startTime) + "ms."); + + startTime = System.currentTimeMillis(); + PrimMST primMST3 = new PrimMST(g3); + endTime = System.currentTimeMillis(); + System.out.println("Test for G3: " + (endTime-startTime) + "ms."); + + startTime = System.currentTimeMillis(); + PrimMST primMST4 = new PrimMST(g4); + endTime = System.currentTimeMillis(); + System.out.println("Test for G4: " + (endTime-startTime) + "ms."); + + System.out.println(); + + + // Test Kruskal MST + System.out.println("Test Kruskal MST:"); + + startTime = System.currentTimeMillis(); + KruskalMST kruskalMST1 = new KruskalMST(g1); + endTime = System.currentTimeMillis(); + System.out.println("Test for G1: " + (endTime-startTime) + "ms."); + + startTime = System.currentTimeMillis(); + KruskalMST kruskalMST2 = new KruskalMST(g2); + endTime = System.currentTimeMillis(); + System.out.println("Test for G2: " + (endTime-startTime) + "ms."); + + startTime = System.currentTimeMillis(); + KruskalMST kruskalMST3 = new KruskalMST(g3); + endTime = System.currentTimeMillis(); + System.out.println("Test for G3: " + (endTime-startTime) + "ms."); + + startTime = System.currentTimeMillis(); + KruskalMST kruskalMST4 = new KruskalMST(g4); + endTime = System.currentTimeMillis(); + System.out.println("Test for G4: " + (endTime-startTime) + "ms."); + + System.out.println(); + } + + + +} diff --git a/src/com/zejian/structures/Graph/WeightGraph/PrimMST.java b/src/com/zejian/structures/Graph/WeightGraph/PrimMST.java index fe54af8..28e3d11 100644 --- a/src/com/zejian/structures/Graph/WeightGraph/PrimMST.java +++ b/src/com/zejian/structures/Graph/WeightGraph/PrimMST.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Observable; /** * Created by zejian on 2018/1/30. diff --git a/src/com/zejian/structures/Graph/WeightGraph/WeightSparseGraph.java b/src/com/zejian/structures/Graph/WeightGraph/WeightSparseGraph.java index 48367d6..ec8f26a 100644 --- a/src/com/zejian/structures/Graph/WeightGraph/WeightSparseGraph.java +++ b/src/com/zejian/structures/Graph/WeightGraph/WeightSparseGraph.java @@ -40,7 +40,6 @@ public int E() { public void addEdge(Edge e) { assert e.v() >= 0 && e.v() < V ; assert e.w() >= 0 && e.w() < V ; - g[e.v()].add(new Edge(e)); if( e.v() != e.w() && !directed ) { g[e.w()].add(new Edge(e.w(),e.v(), (Weight) e.wt())); diff --git a/testDirectedWeightG1.txt b/testDirectedWeightG1.txt new file mode 100644 index 0000000..35f0900 --- /dev/null +++ b/testDirectedWeightG1.txt @@ -0,0 +1,9 @@ +5 8 +0 1 5 +0 2 2 +0 3 6 +1 4 1 +2 1 1 +2 4 5 +2 3 3 +3 4 2 From e53d182849724a191f002286d5677a91be2bb5d9 Mon Sep 17 00:00:00 2001 From: zejian Date: Mon, 12 Feb 2018 20:27:23 +0800 Subject: [PATCH 2/6] =?UTF-8?q?O(n=C2=B2)=E7=9A=84=E6=8E=92=E5=BA=8F?= =?UTF-8?q?=E7=AE=97=E6=B3=95=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../structures/Sort/SortTestHelper.java | 112 ++++++++++++++++++ .../structures/Sort/Sort_N_2/BubbleSort.java | 94 +++++++++++++++ .../Sort/Sort_N_2/InsertionSort.java | 99 ++++++++++++++++ .../structures/Sort/Sort_N_2/MainTest.java | 63 ++++++++++ .../Sort/Sort_N_2/SelectionSort.java | 94 +++++++++++++++ .../structures/Sort/Sort_N_2/ShellSort.java | 95 +++++++++++++++ 6 files changed, 557 insertions(+) create mode 100644 src/com/zejian/structures/Sort/SortTestHelper.java create mode 100644 src/com/zejian/structures/Sort/Sort_N_2/BubbleSort.java create mode 100644 src/com/zejian/structures/Sort/Sort_N_2/InsertionSort.java create mode 100644 src/com/zejian/structures/Sort/Sort_N_2/MainTest.java create mode 100644 src/com/zejian/structures/Sort/Sort_N_2/SelectionSort.java create mode 100644 src/com/zejian/structures/Sort/Sort_N_2/ShellSort.java diff --git a/src/com/zejian/structures/Sort/SortTestHelper.java b/src/com/zejian/structures/Sort/SortTestHelper.java new file mode 100644 index 0000000..73aa75c --- /dev/null +++ b/src/com/zejian/structures/Sort/SortTestHelper.java @@ -0,0 +1,112 @@ +package com.zejian.structures.Sort; + +import java.lang.reflect.Method; + +/** + * Created by zejian on 2018/2/12. + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] + * 排序算法性辅助类 + */ +public class SortTestHelper { + + private SortTestHelper(){} + + /** + * 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR] + * @param n + * @param rangeL + * @param rangeR + * @return + */ + public static Integer[] generateRandomArray(int n, int rangeL, int rangeR) { + + assert rangeL <= rangeR; + + Integer[] arr = new Integer[n]; + + for (int i = 0; i < n; i++) + arr[i] = new Integer((int)(Math.random() * (rangeR - rangeL + 1) + rangeL)); + return arr; + } + + // 生成一个近乎有序的数组 + // 首先生成一个含有[0...n-1]的完全有序数组, 之后随机交换swapTimes对数据 + // swapTimes定义了数组的无序程度: + // swapTimes == 0 时, 数组完全有序 + // swapTimes 越大, 数组越趋向于无序 + public static Integer[] generateNearlyOrderedArray(int n, int swapTimes){ + + Integer[] arr = new Integer[n]; + for( int i = 0 ; i < n ; i ++ ) + arr[i] = new Integer(i); + + for( int i = 0 ; i < swapTimes ; i ++ ){ + int a = (int)(Math.random() * n); + int b = (int)(Math.random() * n); + int t = arr[a]; + arr[a] = arr[b]; + arr[b] = t; + } + + return arr; + } + + /** + * 打印arr数组的所有内容 + * @param arr + */ + public static void printArray(Object arr[]) { + + for (int i = 0; i < arr.length; i++){ + System.out.print( arr[i] ); + System.out.print( ' ' ); + } + System.out.println(); + + return; + } + + + /** + * 判断arr数组是否有序 + * @param arr + * @return + */ + public static boolean isSorted(Comparable[] arr){ + + for( int i = 0 ; i < arr.length - 1 ; i ++ ) + if( arr[i].compareTo(arr[i+1]) > 0 ) + return false; + return true; + } + + /** + * 测试sortClassName所对应的排序算法排序arr数组所得到结果的正确性和算法运行时间 + * @param sortClassName + * @param arr + */ + public static > void testSort(String sortClassName, T[] arr){ + + // 通过Java的反射机制,通过排序的类名,运行排序函数 + try{ + // 通过sortClassName获得排序函数的Class对象 + Class sortClass = Class.forName(sortClassName); + // 通过排序函数的Class对象获得排序方法 + Method sortMethod = sortClass.getMethod("sort",new Class[]{Comparable[].class}); + // 排序参数只有一个,是可比较数组arr + Object[] params = new Object[]{arr}; + + long startTime = System.currentTimeMillis(); + // 调用排序函数 + sortMethod.invoke(null,params); + long endTime = System.currentTimeMillis(); + //判断是否有序 + assert isSorted( arr ); + + System.out.println( sortClass.getSimpleName()+ "消耗时间: " + (endTime-startTime) + "ms" ); + } + catch(Exception e){ + e.printStackTrace(); + } + } +} diff --git a/src/com/zejian/structures/Sort/Sort_N_2/BubbleSort.java b/src/com/zejian/structures/Sort/Sort_N_2/BubbleSort.java new file mode 100644 index 0000000..f1878b3 --- /dev/null +++ b/src/com/zejian/structures/Sort/Sort_N_2/BubbleSort.java @@ -0,0 +1,94 @@ +package com.zejian.structures.Sort.Sort_N_2; + +/** + * Created by zejian on 2018/2/12. + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] + * 冒泡排序算法原理:比较两个相邻的元素,将值大的元素交换至右端。 + * 核心思想:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数, + * 将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数, + * 将小数放前,大数放后。重复第一趟步骤,直至全部排序完成。 + * + * + * 时间复杂度分析: + * 最好的情况是只需扫描一趟,比较 N 次,没有数据移动,时间复杂度为O(N) + * 最坏的情况是数据序列随机排列或者反序排列,需要 N-1 趟扫描 ,那么对于移动数据和比较次数都如下 + * 第一趟:比较 N - 1 次 + * 第二趟:比较 N - 2 次 + * 第三趟:比较 N - 3 次 + * ..... + * 第N-1趟:比较 1 次 + * + * N-1趟的总次数: (N-1) + (N-2) + (N-3) + ... + 1 = (N-1)*(N-2)/2 + * 注意对于移动数据和比较次数都是(N-1)*(N-2)/2 ≈ N² + * 所以最坏的情况下时间杂度为 O(N²) + * 即冒泡排序的时间复杂度在O(N) ~ O(N²) 之间. + * 同时冒泡排序算法是稳定的. + */ +public class BubbleSort { + + /** + * 未优化版本 + * @param array + * @param + */ + public static > void sort1(T[] array){ + + assert array != null; + + for (int i = 0; i 0){ + swap(array,j,j+1); + } + } + } + } + + /** + * 冒泡排序优化版,思路,如果在某轮比较中,再没有发生过交换,那么说明该数组元素已是 + * 有序,没有必要再进行循环操作.这里我们通过一个flag标志符记录. + * @param array + * @param + */ + public static > void sort(T[] array){ + + assert array != null; + boolean exchange = true;//标记是否发生元素交换 + for (int i = 0; i 0){ + swap(array,j,j+1); + exchange = true; + } + } + } + } + + private static > void swap(T[] arr, int i, int j) { + T t = arr[i]; + arr[i] = arr[j]; + arr[j] = t; + } + + + public static void main(String[] args) { + + Integer[] arr = {10,9,8,7,6,5,4,3,2,1}; + BubbleSort.sort(arr); + for( int i = 0 ; i < arr.length ; i ++ ){ + System.out.print(arr[i]); + System.out.print(' '); + } + System.out.println(); + +// +// // 测试排序算法辅助函数 +// int N = 20000; +// Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000); +// SortTestHelper.testSort("com.zejian.structures.Sort.Sort_N_2.SelectionSort", arr); + + } +} diff --git a/src/com/zejian/structures/Sort/Sort_N_2/InsertionSort.java b/src/com/zejian/structures/Sort/Sort_N_2/InsertionSort.java new file mode 100644 index 0000000..1fca284 --- /dev/null +++ b/src/com/zejian/structures/Sort/Sort_N_2/InsertionSort.java @@ -0,0 +1,99 @@ +package com.zejian.structures.Sort.Sort_N_2; + +import com.zejian.structures.Sort.SortTestHelper; + +/** + * Created by zejian on 2018/2/12. + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] + * 插入排序算法核心思想: + * 检查第i个元素,如果在它的左边的元素比它大,进行交换,然后继续下去与前一个元素比较, + * 直到这个元素的左边元素比它还要小,就停止。插入排序法主要的回圈有两个变数:i和j, + * 每一次执行这个回圈,就会将第i个数字放到左边恰当的位置去。 + * + * 分成两组 + * 一组A已排序,一组B未排序,不断从B组中取出元素与A组元素比较,并交互位置. + * 初始化时,第一组A只有一个元素,即第一个元素 + * 未排序组B,则为从第二个元素到最后一个元素. N-1个元素 + * 每轮循环比较后,A组元素总会增加已排序的元素,而B组总会减少1个未排序的元素 + * + * 时间复杂度分析: + * 在最坏情况下,数组完全逆序,插入第2个元素时要考察前1个元素,插入第3个元素时, + * 要考虑前2个元素,……,插入第N个元素,要考虑前 N - 1 个元素。因此,最坏情况下的比较次数是 + * 1 + 2 + 3 + ... + (N - 1) = N * (N-1) / 2 ≈ N² + * 所以最坏情况下的复杂度为 O(N²) + * 最好情况下,数组已经是有序的,只需要N-1次比较和0次交换 + * 因此最好情况下,插入排序的时间复杂度为O(N)。 + * + * 注意插入排序与选择排序的不同是,插入排序每次发现数组有序会提前结束, + * 而选择排序每次总会从头到尾找出最小的元素,因此一般情况下,插入排序的效率 + * 总是高于选择排序. + * + * 尤其在数组有序的情况下,插入排序算法的效率十分高,达到O(N)级别. + * + */ +public class InsertionSort { + + /** + * 插入排序算法,未优化版,每次比较都进行swap + * @param array + */ + public static > void sort1(T[] array){ + assert array != null; + //从第2个元素开始 + for (int i = 1; i < array.length ; i++) { + //元素进行比较排序 + for (int j = i; j > 0 && array[j].compareTo(array[j-1]) < 0 ; j--) { + swap(array,j,j-1); + } + } + } + + /** + * 插入排序算法,优化版,在确定交换的元素后再进行交换位置 + * 每次比较时先不交换,在最后确定最终交换位置时再执行交换. + * @param array + */ + public static > void sort(T[] array){ + assert array != null; + //从第2个元素开始 + for (int i = 1; i < array.length ; i++) { + T e = array[i];//先记录要比较的元素,确定其交换的位置后再移动到对应位置 + int j;//记录j,最后交换时使用 + //普通写法 +// for (j = i; j > 0 ; j--) { +// if(array[j-1].compareTo(e) > 0){ +// array[j] = array[j-1]; +// } +// } + //更优雅的写法,(array[j-1].compareTo(e) > 0)这个比较确保了发现前面没有更大元素就停止,这点与选择排序不同. + for (j = i; j > 0 && array[j-1].compareTo(e) > 0; j--) { + array[j] = array[j-1]; + } + array[j] = e; + } + } + + + private static > void swap(T[] arr, int i, int j) { + T t = arr[i]; + arr[i] = arr[j]; + arr[j] = t; + } + + public static void main(String[] args) { + +// Integer[] arr = {10,9,8,7,6,5,4,3,2,1}; +// InsertionSort.sort2(arr); +// for( int i = 0 ; i < arr.length ; i ++ ){ +// System.out.print(arr[i]); +// System.out.print(' '); +// } +// System.out.println(); + + int N = 20000; + Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_N_2.InsertionSort", arr); + + + } +} diff --git a/src/com/zejian/structures/Sort/Sort_N_2/MainTest.java b/src/com/zejian/structures/Sort/Sort_N_2/MainTest.java new file mode 100644 index 0000000..201972d --- /dev/null +++ b/src/com/zejian/structures/Sort/Sort_N_2/MainTest.java @@ -0,0 +1,63 @@ +package com.zejian.structures.Sort.Sort_N_2; + +import com.zejian.structures.Sort.SortTestHelper; + +import java.util.Arrays; + +/** + * Created by zejian on 2018/2/12. + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] + * 测试插入排序和选择排序的性能 + * 优化后,插入排序比选择排序性能略好 + * 对于有序性强的数组,插入排序远远优于选择排序 + * + */ +public class MainTest { + + public static void main(String[] args) { + + int N = 20000; + + // 测试1 一般测试 + System.out.println("Test for random array, size = " + N + " , random range [0, " + N + "]"); + + Integer[] arr1 = SortTestHelper.generateRandomArray(N, 0, N); + Integer[] arr2 = Arrays.copyOf(arr1, arr1.length); + Integer[] arr3 = Arrays.copyOf(arr1, arr1.length); + + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_N_2.SelectionSort", arr1); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_N_2.InsertionSort", arr2); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_N_2.ShellSort", arr3); + + System.out.println(); + + + // 测试2 有序性更强的测试 + System.out.println("Test for more ordered random array, size = " + N + " , random range [0,3]"); + + arr1 = SortTestHelper.generateRandomArray(N, 0, 3); + arr2 = Arrays.copyOf(arr1, arr1.length); + arr3 = Arrays.copyOf(arr1, arr1.length); + + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_N_2.SelectionSort", arr1); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_N_2.InsertionSort", arr2); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_N_2.ShellSort", arr3); + + System.out.println(); + + + // 测试3 测试近乎有序的数组 + int swapTimes = 100; + System.out.println("测试近乎有序的数组, size = " + N + " , swap time = " + swapTimes); + + arr1 = SortTestHelper.generateNearlyOrderedArray(N, swapTimes); + arr2 = Arrays.copyOf(arr1, arr1.length); + arr3 = Arrays.copyOf(arr1, arr1.length); + + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_N_2.SelectionSort", arr1); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_N_2.InsertionSort", arr2); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_N_2.ShellSort", arr3); + + return; + } +} diff --git a/src/com/zejian/structures/Sort/Sort_N_2/SelectionSort.java b/src/com/zejian/structures/Sort/Sort_N_2/SelectionSort.java new file mode 100644 index 0000000..c16bebc --- /dev/null +++ b/src/com/zejian/structures/Sort/Sort_N_2/SelectionSort.java @@ -0,0 +1,94 @@ +package com.zejian.structures.Sort.Sort_N_2; + +import com.zejian.structures.Sort.SortTestHelper; + +/** + * Created by zejian on 2018/2/12. + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] + * 选择排序算法核心思想:首先,在数组中找到最小的那个元素,将其与数组中第一个元素交换位置 + * (如果恰好第一个位置就是最小元素那么它将与自己交换位置).第一轮结束,接着在剩余的元素中 + * 寻找最小的那个元素,再与第二个位置的元素交换位置,第二轮结束.如此重复,直到数组排序完成. + * + * 时间复杂度分析:对于长度为N的数组,选择排序大概需要 N²/2 次比较和 N 次交换 + * 第一次比较需要 : N-1 次 + * 第二次比较需要 : N-2 次 + * 第三次比较需要 : N-3 次 + * ..... + * 第N-1次比较需要 : 1 次 + * + * 那么对于长度为N的数组进行排序,大约需要 + * (N-1) + (N-2) + (N-3) + ... + 1 = N * (N-1) / 2 + * 所以选择排序总时间复杂度为 + * N * (N-1) / 2 + N ≈ N²/2 + */ +public class SelectionSort { + + public static void selectionSort(int[] array) { + assert array != null; + + for (int i = 0; i < array.length; i++) { + int minIndex = i; + for (int k = i + 1; k < array.length; k++) { + //找到最小的元素的索引并赋值给minIndex + if (array[minIndex] > array[k]) { + minIndex = k; + } + } + //交换位置 + swap(array, i, minIndex); + } + } + + /** + * 比较实现了Comparable的数据类型 + * @param array + */ + public static > void sort(T[] array) { + assert array != null; + + for (int i = 0; i < array.length; i++) { + int minIndex = i; + for (int k = i + 1; k < array.length; k++) { + //找到最小的元素的索引并赋值给minIndex + if (array[minIndex].compareTo(array[k]) > 0) { + minIndex = k; + } + } + //交换位置 + swap(array, i, minIndex); + } + } + + + private static void swap(int[] arr, int i, int j) { + int t = arr[i]; + arr[i] = arr[j]; + arr[j] = t; + } + + private static > void swap(T[] arr, int i, int j) { + T t = arr[i]; + arr[i] = arr[j]; + arr[j] = t; + } + + + public static void main(String[] args) { + +// int[] arr = {10,9,8,7,6,5,4,3,2,1}; +// SelectionSort.selectionSort(arr); +// for( int i = 0 ; i < arr.length ; i ++ ){ +// System.out.print(arr[i]); +// System.out.print(' '); +// } +// System.out.println(); + + + // 测试排序算法辅助函数 + int N = 20000; + Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_N_2.SelectionSort", arr); + + } + +} diff --git a/src/com/zejian/structures/Sort/Sort_N_2/ShellSort.java b/src/com/zejian/structures/Sort/Sort_N_2/ShellSort.java new file mode 100644 index 0000000..8f7264a --- /dev/null +++ b/src/com/zejian/structures/Sort/Sort_N_2/ShellSort.java @@ -0,0 +1,95 @@ +package com.zejian.structures.Sort.Sort_N_2; + +import com.zejian.structures.Sort.SortTestHelper; + +/** + * Created by zejian on 2018/2/12. + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] + * 希尔排序核心思想:希尔排序是一种基于插入排序优化的排序算法.其思想是利用分组的方式,对每个 + * 小分组进行插入排序,而这个分组的区间长度为h,而由h区间分组而成的小组是相互独立的,我们在 + * 小组内部执行插入排序操作使用该小组有序.然后使h的值变小,再次执行上述操作,直到h=1为止. + * 至于h该如何取值,是一个数学的难题,这里我们使用推荐的增量序列 + * increment sequence: 1, 4, 13, 40, 121, 364, 1093... + * 即 h = 3*h + 1 + * + * 例如我们需要对以下数组进行排序 + * 8 9 2 7 3 5 4 6 + * 假设我们的h=4 , 那么分组将会是[8 3] [9 5] [2 4] [7 6] + * 然后对每个小组进行单独的插入排序,即排序后如下 + * [3 8] [5 9] [2 4] [6 7] + * 再第1轮完成后合并 3 8 5 9 2 4 6 7 + * 然后将h再减少, h = h / 3 再进行如上操作 + * 显然 h = h / 3 = 1 此时h已为1,无法更小了,那么希尔排序的工作就基本完成, + * 同时当h=1时也意味着该排序完全变为插入排序算法,也就是说,当h=1时,我们只需要对 + * 刚才经过希尔操作后数组进行最后一次插入排序就可以使用数组完全有序了 + * 3 8 5 9 2 4 6 7 + * 插入排序后(h=1) + * 2 3 4 5 6 7 8 9 + * + * 之所以能这样操作,是因为无序数组经过希尔操作后已变得几乎有序,此时当h=1时变为插入排序的 + * 排序效率也非常高.当排序数组的数据规模十分大时,希尔排序的更能体现出优势. + * 之所以需要希尔排序算法,是因为对于大规模乱序的数组插入排序的效率会显得很低,因为它只会交 + * 换相邻的元素,元素只能一点一点从数组的一端移动到另外一端.如对10000个数据排序恰好最小的 + * 元素在数组的末尾,那么操作时只能从尾部一点点向前移动,即经常N-1次操作.如果在此之前对数组 + * 进行希尔操作,直到h=1时,数组将会变得近乎有序,那么当h=1时执行插入排序的效率就非常高了. + * + * 希尔排序算法的时间复杂小于O(N^2),达到O(N^3/2)或O(N^4/3)等...,已突破N^2的瓶颈 + */ +public class ShellSort { + + /** + * 希尔排序算法 + * @param array + * @param + */ + public static > void sort(T[] array){ + assert array != null; + int h = 1; + int N = array.length; + //根据增量序列取值: 1, 4, 13, 40, 121, 364, 1093... + //h为分组的区间 + while (h < N/3){ + h = 3*h + 1; + } + //最后1轮即h=1时,对整个数组执行插入排序算法 + while (h>=1){ + //对分组进行插入排序 + for (int i = h; i < N ; i++) { + // 对 arr[i], arr[i-h], arr[i-2*h], arr[i-3*h]... 使用插入排序 + T e = array[i]; + int j = i; + for (; j >= h && e.compareTo(array[j-h]) < 0; j -= h) { + array[j] = array[j-h]; + } + //注意j的值已减去h,这里无需执行j-h操作 + array[j] = e; + } + + h /= 3; + + } + + } + + /** + * 测试 + * @param args + */ + public static void main(String[] args) { + +// Integer[] arr = {10,9,8,7,6,5,4,3,2,1}; +// ShellSort.sort(arr); +// for( int i = 0 ; i < arr.length ; i ++ ){ +// System.out.print(arr[i]); +// System.out.print(' '); +// } +// System.out.println(); + + int N = 20000; + Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_N_2.ShellSort", arr); + + + } + +} From aec285533abe45aaa5d3387e5b737e0e8dd13ae4 Mon Sep 17 00:00:00 2001 From: zejian Date: Tue, 13 Feb 2018 23:56:55 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E6=97=B6=E9=97=B4=E5=A4=8D=E6=9D=82?= =?UTF-8?q?=E5=BA=A6=E4=B8=BAN*LogN=E7=BA=A7=E5=88=AB=E7=9A=84=E7=9A=84?= =?UTF-8?q?=E6=8E=92=E5=BA=8F=E7=AE=97=E6=B3=95-=E5=BD=92=E5=B9=B6?= =?UTF-8?q?=E6=8E=92=E5=BA=8F=E5=92=8C=E6=99=AE=E9=80=9A=E5=BF=AB=E9=80=9F?= =?UTF-8?q?=E6=8E=92=E5=BA=8F=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../structures/Sort/SortTestHelper.java | 31 +++ .../structures/Sort/Sort_NLogN/MergeSort.java | 205 ++++++++++++++++++ .../Sort/Sort_NLogN/MergeSortBottomUp.java | 100 +++++++++ .../structures/Sort/Sort_NLogN/QuickSort.java | 119 ++++++++++ .../Sort/Sort_N_2/InsertionSort.java | 17 ++ 5 files changed, 472 insertions(+) create mode 100644 src/com/zejian/structures/Sort/Sort_NLogN/MergeSort.java create mode 100644 src/com/zejian/structures/Sort/Sort_NLogN/MergeSortBottomUp.java create mode 100644 src/com/zejian/structures/Sort/Sort_NLogN/QuickSort.java diff --git a/src/com/zejian/structures/Sort/SortTestHelper.java b/src/com/zejian/structures/Sort/SortTestHelper.java index 73aa75c..6c2574d 100644 --- a/src/com/zejian/structures/Sort/SortTestHelper.java +++ b/src/com/zejian/structures/Sort/SortTestHelper.java @@ -109,4 +109,35 @@ public static > void testSort(String sortClassName, T[] e.printStackTrace(); } } + + + /** + * 测试sortClassName所对应的排序算法排序arr数组所得到结果的正确性和算法运行时间 + * @param sortClassName + * @param arr + */ + public static > void testSort(String sortClassName, String methodName ,T[] arr){ + + // 通过Java的反射机制,通过排序的类名,运行排序函数 + try{ + // 通过sortClassName获得排序函数的Class对象 + Class sortClass = Class.forName(sortClassName); + // 通过排序函数的Class对象获得排序方法 + Method sortMethod = sortClass.getMethod(methodName,new Class[]{Comparable[].class}); + // 排序参数只有一个,是可比较数组arr + Object[] params = new Object[]{arr}; + + long startTime = System.currentTimeMillis(); + // 调用排序函数 + sortMethod.invoke(null,params); + long endTime = System.currentTimeMillis(); + //判断是否有序 + assert isSorted( arr ); + + System.out.println( sortClass.getSimpleName()+"类方法:"+methodName+"消耗时间: " + (endTime-startTime) + "ms" ); + } + catch(Exception e){ + e.printStackTrace(); + } + } } diff --git a/src/com/zejian/structures/Sort/Sort_NLogN/MergeSort.java b/src/com/zejian/structures/Sort/Sort_NLogN/MergeSort.java new file mode 100644 index 0000000..6401d6b --- /dev/null +++ b/src/com/zejian/structures/Sort/Sort_NLogN/MergeSort.java @@ -0,0 +1,205 @@ +package com.zejian.structures.Sort.Sort_NLogN; + +import com.zejian.structures.Sort.SortTestHelper; +import com.zejian.structures.Sort.Sort_N_2.InsertionSort; + +import java.lang.reflect.Array; +import java.util.Arrays; + +/** + * Created by zejian on 2018/2/12. + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] + * 归并排序算法,采用的是分而治之的策略.其核心思想是先把要排序的数组元素不断得进行一分为二, + * 直到每个分组只有一个元素时,由于该分组只有一个元素就本身就是有序的数组,然后再将有序的数组 + * 两两合并为新的有序数组,合并完成后如果还有分组就重复前面的操作将有序数组再次合并为新的有 + * 序数组,直到合并为一个数组为止.最终数组就是有序数组.如下演示: + * 8 5 1 2 7 6 3 4 其中l=0 r=7 , mid=(l+r)/2 =3 + * 根据下标3分为两组: [8 5 1 2] [7 6 3 4] + * + * 注意:先处理左边分组(继续分组操作直到分组的元素个数为1) + * [8 5 1 2] ==> L=0 R=3 mid=(l+r)/2=1 + * [8 5] [1 2] + * [8 5] ==> L=0 R=1 mid=0; + * [8] [5] ==> L=0 R=0 L>=R 分组结束 + * 对有序数组[8] [5]进行归并[5 8] + * [1 2] ==> L=2 R=3 mid=2 + * [1] [2] ==> L=2 R=2 L>=R 分组结束 + * 对有序数组[1] [2]进行归并[1 2] + * 再对有序数组[5 8] [1 2]进行合并[1 2 5 8] + * 左边最终处理结果:[1 2 5 8] + * + * 再处理右边分组 + * [7 6 3 4] ==> L=4 R=7 mid=5 + * [7 6] [3 4] + * [7 6] ==> L=4 R=5 mid=4 + * [7] [6] ==> L=4 R=4 L>=R分组结束 + * 合并有序数组[7] [6] => [6 7] + * 同理: [3] [4] => [3 4] + * 合并有序数组[6 7] [3 4] ==> [3 4 6 7] + * 右边处理完成 + * + * 最终合并有序数组[1 2 5 8] 和 [3 4 6 7] + * [1 2 3 4 5 6 7 8] + * + * + * 归并排序时间复杂度分析: + * 对于数组进行分组直到数组元素个数为1,采用的是类似与二叉树查找的操作,因此假设数组元素个数为N, + * 那么N个元素分组后最多有 ㏒N 层.由于每层分组后最终都需要对数组执行归并操作,而每层处理的归并 + * 操作(该操作就是让数组中元素交换位置)总元素是是固定的为N. + * 因此归并排序的时间复杂度为 N * ㏒N + * + * [8 5 1 2 7 6 3 4] + * 1层 [8 5 1 2] [7 6 3 4] + * 2层 [8 5] [1 2] [7 6] [3 4] + * 3层 [8] [5] [1] [2] [7] [6] [3] [4] + * 执行归并操作 + * [5 8] [1 2] [6 7] [3 4] 每次归并操作元素个数为N + * [1 2 5 8] [3 4 6 7] + * [1 2 3 4 5 6 7 8] + * + * + * 归并排序算法和快速排序算法是java.util.Arrays中使用的排序算。 + * 对于一般的基本数据类型,Arrays.sort函数使用双轴快速排序算法, + * 而对于对象类型使用归并排序(准确的说使用的是TimSort排序算法, + * 它是归并排序的优化版本)。这样做的原因有两点 + * 第一个原因,归并排序是稳定的而快速排序不是稳定的。 + * 第二个原因,对于基本数据类型,排序的稳定性意义不大, + * 但对于复合数据类型(比如对象)排序的稳定性就能帮助我们保持排序结果的某些性质。 + * + */ +public class MergeSort { + + /** + * 未优化版归并排序 + * @param array + * @param + */ + public static > void sort(T[] array){ + assert array != null; + int n = array.length; + mergeSort(array,0,n-1); + } + + /** + * 优化版归并排序 + * @param array + * @param + */ + public static > void sortOptimize(T[] array){ + assert array != null; + int n = array.length; + mergeSortOptimize(array,0,n-1); + } + + + /** + * 归并排序算法优化版 + */ + private static > void mergeSortOptimize(T[] array ,int l , int r){ + + // 优化: 对于小规模数组, 使用插入排序,在较小的数据范围内,数据近乎有序 + // 可能性非常,采用插入排序能提高效率 + if( r - l <= 15 ){ + InsertionSort.sort(array, l, r); + return; + } + + //计算分组间距 + int mid = l + (r - l)/2; + + //递归分组 + mergeSort(array,l,mid); + mergeSort(array,mid+1,r); + + /** + * 优化: 对于arr[mid] <= arr[mid+1]的情况,不进行merge + * 对于近乎有序的数组非常有效,但是对于一般情况,有一定的性能损失 + */ + if( array[mid].compareTo(array[mid+1]) > 0 ) { + //两边都分组完成即分组后数组元素只有一个后开始合并 + merge(array, l, mid, r); + } + } + + /** + * 归并排序算法(未优化版) + * @param array + * @param l + * @param r + * @param + */ + private static > void mergeSort(T[] array ,int l , int r){ + //递归结束条件(分组后数组只有一个元素时不再分组) + if(l>=r){ + return; + } + + //计算分组间距 + int mid = l + (r - l)/2; + + //递归分组 + mergeSort(array,l,mid); + mergeSort(array,mid+1,r); + //两边都分组完成即分组后数组元素只有一个后开始合并 + merge(array, l, mid, r); + } + + /** + * 合并函数 + * @param array + * @param l + * @param mid + * @param r + * @param + */ + public static > void merge(T[] array , int l , int mid , int r){ + int len = r-l+1; + //创建临时数据辅助归并 + T[] temp = (T[]) Array.newInstance(array.getClass().getComponentType(),len); + //把需要归并的元素全赋值给temp; + for(int i=l ; i <= r ; i++){ + temp[i-l] = array[i]; + } + + int i = l; + int j = mid+1; + //执行归并操作(O(N)级别) + for(int k=l ; k <= r ; k++){ + if(i > mid){ //说明左边的元素已全部归并完成 + array[k] = temp[j-l]; + j++; + }else if(j > r){ //说明右边的元素已全部归并完成 + array[k] = temp[i-l]; + i++; + }else if(temp[i-l].compareTo(temp[j-l]) > 0){//左边的元素大于右边 + array[k] = temp[j-l]; + j++; + }else if(temp[i-l].compareTo(temp[j-l]) < 0){ //右边的元素大于左边 + array[k] = temp[i-l]; + i++; + } + } + } + + public static void main(String[] args) { +// +// Integer[] arr = {10,9,8,7,6,5,4,3,2,1}; +// MergeSort.sort(arr); +// for( int i = 0 ; i < arr.length ; i ++ ){ +// System.out.print(arr[i]); +// System.out.print(' '); +// } +// System.out.println(); + + //对于近乎有序的数组,优化后的归并排序效率更高 + int N = 200000; + Integer[] arr1 = SortTestHelper.generateNearlyOrderedArray(N, 4); + Integer[] arr2 = Arrays.copyOf(arr1, arr1.length); + + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.MergeSort","sort", arr1); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.MergeSort","sortOptimize",arr2); + + } + + +} diff --git a/src/com/zejian/structures/Sort/Sort_NLogN/MergeSortBottomUp.java b/src/com/zejian/structures/Sort/Sort_NLogN/MergeSortBottomUp.java new file mode 100644 index 0000000..c572415 --- /dev/null +++ b/src/com/zejian/structures/Sort/Sort_NLogN/MergeSortBottomUp.java @@ -0,0 +1,100 @@ +package com.zejian.structures.Sort.Sort_NLogN; + +import com.zejian.structures.Sort.Sort_N_2.InsertionSort; + +/** + * Created by zejian on 2018/2/13. + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] + * 自底向上的归并排序算法 + * 自底向上的归并排序算法的思想就是数组中先一个一个归并成两两有序的数组, + * 两两有序的数组归并成四个四个有序的数组,然后四个四个有序的序列归并八 + * 个八个有序的数组,以此类推,直到,归并的长度大于整个数组的长度,此时 + * 整个数组有序。需要注意的是数组按照归并长度划分,最后一个子数组可能不 + * 满足长度要求,这种情况需要注意。自顶下下的归并排序算法一般用递归 + * 来实现,而自底向上可以用循环来实现。 + * ∧ + * [1 2 3 4 5 6 7 8] ‖上 最后合并成一个有序数组 + * [1 2 5 8] [3 4 6 7] ‖向 再归并成四四分组 + * [5 8] [1 2] [6 7] [3 4] ‖底 先按两两分组,然后排序 + * [8] [5] [1] [2] [7] [6] [3] [4] ‖自 一一分组然后归并 + * [8 5 1 2 7 6 3 4] ‖ 未分组 + * + */ +public class MergeSortBottomUp { + + + public static > void sort(T[] array){ + assert array != null; + int len = array.length; + //分组循环排序,sz表示每组分组的个数,第一轮分组数组个数为1 + for (int sz = 1; sz < len ; sz += sz) { + //对分组进行两两归并 + //由于每次归并的是两个分组个数,所以归并的间隔是 sz + sz = 2 * sz + for (int i = 0; i < len ; i += 2*sz) { + //i+sz 代表分组的下标最大值,由于下标从0开始,所以需要i+sz-1 + //用于归并的第二组起始点是i+sz,结束点:i + sz - 1 + sz + //对array[i....i+sz-1] 和 array[i+sz.....i+sz+sz-1]进行归并 + int l = i; + int mid = i+sz-1; + int r = i+sz+sz-1; + //进行合并操作,Math.min(r,len-1)防止数组越界,存在最后一个分组不符合整数的情况 + MergeSort.merge(array,l,mid,Math.min(r,len-1)); + } + } + } + + /** + * 优化版 + * @param array + * @param + */ + public static > void sortOptimize(T[] array){ + assert array != null; + int len = array.length; + + //优化: 对于小数组, 使用插入排序优化sz是分组的大小 + for( int sz = 0 ; sz < len ; sz += 16 ){ + //Math.min(i+16-1,len-1)防止数组越界,存在最后一个分组不符合整数的情况 + InsertionSort.sort(array,sz,Math.min(sz+16-1,len-1)); + } + + //分组循环排序,sz表示每组分组的个数,第一轮分组数组个数为1 + for (int sz = 16; sz < len ; sz += sz) { + //对分组进行两两归并 + //由于每次归并的是两个分组个数,所以归并的间隔是 sz + sz = 2 * sz + for (int i = 0; i < len ; i += 2*sz) { + //i+sz 代表分组的下标最大值,由于下标从0开始,所以需要i+sz-1 + //用于归并的第二组起始点是i+sz,结束点:i + sz - 1 + sz + //对array[i....i+sz-1] 和 array[i+sz.....i+sz+sz-1]进行归并 + int l = i; + int mid = i+sz-1; + int r = i+sz+sz-1; + /** + * 优化:对于arr[mid] <= arr[mid+1]的情况,不进行merge + */ + if(array[mid].compareTo(array[mid+1]) > 0) { + //进行合并操作 + MergeSort.merge(array, l, mid, Math.min(r, len - 1)); + } + } + } + } + + + /** + * Test + * @param args + */ + public static void main(String[] args) { + Integer[] arr = {10,9,8,7,6,5,4,3,2,1}; + MergeSortBottomUp.sortOptimize(arr); + for( int i = 0 ; i < arr.length ; i ++ ){ + System.out.print(arr[i]); + System.out.print(' '); + } + System.out.println(); + } + + + +} diff --git a/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort.java b/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort.java new file mode 100644 index 0000000..44e2c68 --- /dev/null +++ b/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort.java @@ -0,0 +1,119 @@ +package com.zejian.structures.Sort.Sort_NLogN; + +import com.zejian.structures.Sort.SortTestHelper; + +import java.util.Arrays; + +/** + * Created by zejian on 2018/2/13. + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] + * 普通快速排序算法基本思路 + * 1.从数组arr中随机挑选一个元素,定为基准点arr[p] + * 2.对数组arr进行从新排序,使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p], + * 这个操作过程称为分区(partition) + * 3.使用递归的方式重复步骤1和2分别处理左侧数组arr[l...p-1] 和 右侧数组arr[p+1...r], + * 直到无法分区,此时数组排序完成. + * + * arr[l+1...j] < v < arr[j+1...i) + * 时间复杂度分析: + * 快速排序每次将待排序数组分为两个部分,在理想状况下, + * 每一次都将待排序数组划分成等长两个部分,则需要logN次划分 + * 即存在N层,而每层需要处理的元素个数都是一样的即N,此时时间 + * 复杂度为 N*logN + * 而在最坏情况下,即数组已经有序或大致有序的情况下,每次划分 + * 只能减少一个元素,快速排序将不幸退化为冒泡排序,因为每次分 + * 区只可能交互一个元素,时间复杂度为O(N²) + * 所以快速排序时间复杂度下界为O(N*logN),最坏情况为O(N²)。 + * 在实际应用中,快速排序的平均时间复杂度为O(nlogn)。 + */ +public class QuickSort { + + public static > void sort(T[] arr){ + assert arr != null; + quickSort(arr,0,arr.length-1); + } + + /** + * 使用递归实现快速排序 + * @param arr + * @param l + * @param r + * @param + */ + public static > void quickSort(T[] arr,int l,int r){ + //如果排序的数组元素个数少于16直接使用插入排序即可(优化点) +// if(r-l < 16){ +// InsertionSort.sort(arr,l,r); +// return; +// } + + if (l >= r){ + return; + } + + //获取分区的基准点的数组下标p + int p = partition(arr,l,r); + //使用递归的方式继续进行分区 + quickSort(arr,l,p-1); //处理左侧数组 + quickSort(arr,p+1,r); //处理右侧数组 + + } + + /** + * 进行分区操作 + * 对arr[l...r]部分进行partition操作 + * 返回p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p] + */ + private static > int partition(T[] arr,int l ,int r){ + //随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot + //之所以要随机选择,是为了防止在出现大规模近乎有序的数据时若只取左边的值可能会 + //造成快速排序算法退化到O(N²)级别,与二叉树退化成链表是同样的道理. + swap(arr,l,(int)(Math.random()*(r-l+1))+l); + //确定分组元素 + T e = arr[l]; + // 进行分区操作,使用得 arr[l+1...j] < v < arr[j+1...i) i是当前正在判断的元素 + int j = l; + //注意i需要从1+1起 + for (int i = l+1; i <= r ; i++) { + //当前元素比基准点元素小,移动到e的左侧. + //当前元素比基准点元素大,无需移动. + if(arr[i].compareTo(e) < 0){ + swap(arr,j+1,i); + j++;//j往后移动一位 + } + } + + //最后将基准点移动到j的位置,形成arr[l...j-1] < arr[j] < arr[j+1...r] + swap(arr,j,l); + //返回基准点下标 + return j; + } + + + private static > void swap(T[] arr, int i, int j) { + T t = arr[i]; + arr[i] = arr[j]; + arr[j] = t; + } + + public static void main(String[] args) { + Integer[] arr = {10,9,8,7,6,5,4,3,2,1}; + QuickSort.sort(arr); + for( int i = 0 ; i < arr.length ; i ++ ){ + System.out.print(arr[i]); + System.out.print(' '); + } + System.out.println(); + + //对于近乎有序的数组,优化后的归并排序效率更高 + int N = 200000; + Integer[] arr1 = SortTestHelper.generateNearlyOrderedArray(N, 4); + Integer[] arr2 = Arrays.copyOf(arr1, arr1.length); + Integer[] arr3 = Arrays.copyOf(arr1, arr1.length); + + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.MergeSort","sort", arr1); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.MergeSort","sortOptimize",arr2); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort","sort",arr3); + + } +} diff --git a/src/com/zejian/structures/Sort/Sort_N_2/InsertionSort.java b/src/com/zejian/structures/Sort/Sort_N_2/InsertionSort.java index 1fca284..7d1423a 100644 --- a/src/com/zejian/structures/Sort/Sort_N_2/InsertionSort.java +++ b/src/com/zejian/structures/Sort/Sort_N_2/InsertionSort.java @@ -74,6 +74,23 @@ public static > void sort(T[] array){ } + public static > void sort(T[] array,int l , int r){ + assert array != null; + + //从第2个元素开始 + for (int i = l+1; i <=r ; i++) { + T e = array[i];//先记录要比较的元素,确定其交换的位置后再移动到对应位置 + int j;//记录j,最后交换时使用 + //更优雅的写法 + //(array[j-1].compareTo(e) > 0)这个比较确保了发现前面没有更大元素就停止,这点与选择排序不同. + for (j = i; j > l && array[j-1].compareTo(e) > 0; j--) { + array[j] = array[j-1]; + } + array[j] = e; + } + } + + private static > void swap(T[] arr, int i, int j) { T t = arr[i]; arr[i] = arr[j]; From f45c5111b6ab6f9cc2a38dd64469e48e5a9695bf Mon Sep 17 00:00:00 2001 From: zejian Date: Wed, 14 Feb 2018 11:56:32 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E6=97=B6=E9=97=B4=E5=A4=8D=E6=9D=82?= =?UTF-8?q?=E5=BA=A6=E4=B8=BAN*LogN=E7=BA=A7=E5=88=AB=E7=9A=84=E7=9A=84?= =?UTF-8?q?=E6=8E=92=E5=BA=8F=E7=AE=97=E6=B3=95-=E5=8F=8C=E8=B7=AF?= =?UTF-8?q?=E5=BF=AB=E9=80=9F=E6=8E=92=E5=BA=8F=E5=92=8C=E4=B8=89=E8=B7=AF?= =?UTF-8?q?=E5=BF=AB=E9=80=9F=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../structures/Sort/Sort_NLogN/MainTest.java | 66 +++++++++ .../structures/Sort/Sort_NLogN/QuickSort.java | 17 +-- .../Sort/Sort_NLogN/QuickSort2Ways.java | 125 ++++++++++++++++++ .../Sort/Sort_NLogN/QuickSort3Ways.java | 104 +++++++++++++++ .../Sort/Sort_N_2/InsertionSort.java | 38 ++++-- 5 files changed, 329 insertions(+), 21 deletions(-) create mode 100644 src/com/zejian/structures/Sort/Sort_NLogN/MainTest.java create mode 100644 src/com/zejian/structures/Sort/Sort_NLogN/QuickSort2Ways.java create mode 100644 src/com/zejian/structures/Sort/Sort_NLogN/QuickSort3Ways.java diff --git a/src/com/zejian/structures/Sort/Sort_NLogN/MainTest.java b/src/com/zejian/structures/Sort/Sort_NLogN/MainTest.java new file mode 100644 index 0000000..83c3b9b --- /dev/null +++ b/src/com/zejian/structures/Sort/Sort_NLogN/MainTest.java @@ -0,0 +1,66 @@ +package com.zejian.structures.Sort.Sort_NLogN; + +import com.zejian.structures.Sort.SortTestHelper; + +import java.util.Arrays; + +/** + * Created by zejian on 2018/2/12. + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] + * 测试归并排序,普通快速排序 双路快速排序 三路快速排序的性能效率 + * + */ +public class MainTest { + + public static void main(String[] args) { + + int N = 200000; + + // 测试1 一般测试 + System.out.println("测试随机无序的数组, size = " + N + " , random range [0, " + N + "]"); + + Integer[] arr1 = SortTestHelper.generateRandomArray(N, 0, N); + Integer[] arr2 = Arrays.copyOf(arr1, arr1.length); + Integer[] arr3 = Arrays.copyOf(arr1, arr1.length); + Integer[] arr4 = Arrays.copyOf(arr1, arr1.length); + + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.MergeSort","sortOptimize",arr1); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort","sort",arr2); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort2Ways","quickSort2Ways",arr3); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort3Ways","quickSort3Ways",arr4); + + System.out.println(); + + + // 测试2 有序性更强的测试 + System.out.println("测试存在大量重复元素的数组排序, size = " + N + " , random range [0,3]"); + + arr1 = SortTestHelper.generateRandomArray(N, 0, 3); + arr2 = Arrays.copyOf(arr1, arr1.length); + arr3 = Arrays.copyOf(arr1, arr1.length); + arr4 = Arrays.copyOf(arr1, arr1.length); + + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.MergeSort","sortOptimize",arr1); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort","sort",arr2); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort2Ways","quickSort2Ways",arr3); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort3Ways","quickSort3Ways",arr4); + + System.out.println(); + + + // 测试3 测试近乎有序的数组 + int swapTimes = 100; + System.out.println("测试近乎有序的数组的数组排序, size = " + N + " , swap time = " + swapTimes); + + arr1 = SortTestHelper.generateNearlyOrderedArray(N, swapTimes); + arr2 = Arrays.copyOf(arr1, arr1.length); + arr3 = Arrays.copyOf(arr1, arr1.length); + arr4 = Arrays.copyOf(arr1, arr1.length); + + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.MergeSort","sortOptimize",arr1); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort","sort",arr2); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort2Ways","quickSort2Ways",arr3); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort3Ways","quickSort3Ways",arr4); + + } +} diff --git a/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort.java b/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort.java index 44e2c68..c5ef77b 100644 --- a/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort.java +++ b/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort.java @@ -1,6 +1,7 @@ package com.zejian.structures.Sort.Sort_NLogN; import com.zejian.structures.Sort.SortTestHelper; +import com.zejian.structures.Sort.Sort_N_2.InsertionSort; import java.util.Arrays; @@ -42,15 +43,15 @@ public static > void sort(T[] arr){ */ public static > void quickSort(T[] arr,int l,int r){ //如果排序的数组元素个数少于16直接使用插入排序即可(优化点) -// if(r-l < 16){ -// InsertionSort.sort(arr,l,r); -// return; -// } - - if (l >= r){ + if(r-l < 16){ + InsertionSort.sort(arr,l,r); return; } +// if (l >= r){ +// return; +// } + //获取分区的基准点的数组下标p int p = partition(arr,l,r); //使用递归的方式继续进行分区 @@ -90,7 +91,7 @@ private static > int partition(T[] arr,int l ,int r){ } - private static > void swap(T[] arr, int i, int j) { + public static > void swap(T[] arr, int i, int j) { T t = arr[i]; arr[i] = arr[j]; arr[j] = t; @@ -106,7 +107,7 @@ public static void main(String[] args) { System.out.println(); //对于近乎有序的数组,优化后的归并排序效率更高 - int N = 200000; + int N = 2000; Integer[] arr1 = SortTestHelper.generateNearlyOrderedArray(N, 4); Integer[] arr2 = Arrays.copyOf(arr1, arr1.length); Integer[] arr3 = Arrays.copyOf(arr1, arr1.length); diff --git a/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort2Ways.java b/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort2Ways.java new file mode 100644 index 0000000..2b82fcb --- /dev/null +++ b/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort2Ways.java @@ -0,0 +1,125 @@ +package com.zejian.structures.Sort.Sort_NLogN; + +import com.zejian.structures.Sort.SortTestHelper; +import com.zejian.structures.Sort.Sort_N_2.InsertionSort; + +import java.util.Arrays; + +/** + * Created by zejian on 2018/2/14. + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] + * 双路快速排序算法:主要解决排序数据中存在大量重复值的情景,因为这种情况下使用前面实现 + * 的普通快速排序,会把等于v的元素归到 >=v 一方 而造成 < v 的一方数据过少 + * arr[l....j] < v 而 [j+1 ... r] >= v [j+1 ... r]包含大量重复的v元素 + * 从而造成分区时树结构失去平衡性,最终可能使普通快速排序由 N*logN 退化为 O(N²) + * + * 双路快速排序就是为了解决该问题而出现的.其基本思路与普通快速排序不同, + * 其将分区设置为 arr[l+1...i) <= v; arr(j...r] >= v + * 这样的话无论是小于v的左侧数组还是大于v的右侧数组都包含了等于v的情况,在这种情况下 + * 即使出现了大量重复元素,也不会导致左右两边分区的数组元素个数差距过大,造成排序性能 + * 效率过低. + * 缺点是重复元素值仍会被交换位置. + * + * 时间复杂度依然是 N*logN 级别 + * + * + */ +public class QuickSort2Ways { + + /** + * 双路快速排序 + * @param arr + * @param + */ + public static > void quickSort2Ways(T[] arr){ + assert arr !=null; + int len = arr.length; + quickSort2Ways(arr,0,len-1); + } + + private static > void quickSort2Ways(T[] arr , int l , int r){ +// //优化:数组个数小于16使用插入排序进行 + if(r-l < 16){ + InsertionSort.sort(arr,l,r); + return; + } + if( l >= r){ + return; + } + + //计算并获取基准点的下标 + int p = partition(arr,l,r); + //继续递归执行 + quickSort2Ways(arr,l,p-1); + quickSort2Ways(arr,p+1,r); + + } + + /** + * 双路快速排序的分区采用不同的分区思路 + * @return + */ + private static > int partition(T[] arr , int l , int r){ + + //随机选取一个基准点并与最左侧位置的元素交换 + QuickSort.swap(arr,l,(int)(Math.random()*(r - l))+l); + + //获取基准点 + T e = arr[l]; + + //下标从l+1开始 + int i = l+1; //arr[l+1 ... i) <= v + int j = r; //arr(j...r] >= v + + //进行分区操作 + while (true){ + //如果当前元素比基准点元素v小,不动,继续循环直到找到比较v大的停止 + while ( i <= r && arr[i].compareTo(e) < 0) i++; + //如果当前元素比基准点元素v大,不动,继续循环直到找到比较v小的停止 + while ( j > l && arr[j].compareTo(e) > 0) j--; + + //如果此时i>j说明已分区完成 + if(i > j){ + break; + } + + //交换i与j的元素 + QuickSort.swap(arr,i,j); + i++; + j--; + } + + + //最后交换基准元素的位置,这里之所以使用j,是因为上述while结束时,i>j + //此时左侧分区j肯定是 arr[l+1...j] 而右侧分区肯定是arr[i...r] + QuickSort.swap(arr,l,j); + + return j; + } + + + public static void main(String[] args) { +// Integer[] arr = {10,9,8,7,6,5,4,3,2,1}; +// QuickSort2Ways.quickSort2Ways(arr); +// for( int i = 0 ; i < arr.length ; i ++ ){ +// System.out.print(arr[i]); +// System.out.print(' '); +// } +// System.out.println(); + + //对于近乎有序的数组,优化后的归并排序效率更高 + int N = 200000; + Integer[] arr1 = SortTestHelper.generateNearlyOrderedArray(N, 4); + Integer[] arr2 = Arrays.copyOf(arr1, arr1.length); + Integer[] arr3 = Arrays.copyOf(arr1, arr1.length); + Integer[] arr4 = Arrays.copyOf(arr1, arr1.length); + System.out.println("处理近乎有序的数组:"); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.MergeSort","sortOptimize",arr1); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort","sort",arr2); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort2Ways","quickSort2Ways",arr3); + + + + } + +} diff --git a/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort3Ways.java b/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort3Ways.java new file mode 100644 index 0000000..987fb93 --- /dev/null +++ b/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort3Ways.java @@ -0,0 +1,104 @@ +package com.zejian.structures.Sort.Sort_NLogN; + +import com.zejian.structures.Sort.SortTestHelper; +import com.zejian.structures.Sort.Sort_N_2.InsertionSort; + +import java.util.Arrays; + +/** + * Created by zejian on 2018/2/14. + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] + * 三路快速排序算法:也是为了解决排序元素中存在大规模重复元素的问题.只不过三路排序算法比 + * 双路快速排序算法性能更佳,因为针对于重复元素,三路快速排序不会再次进行交换位置或者纳入 + * 下一轮快速排序中. + * 算法核心思想是把需要排序的数组分成3部分,分别是 + * arr[l+1 ... lt] arr[v,v,v,v,v,v...] arr[gt ... r] + * 经过一轮分区后,再下一轮中只对arr[l+1 ... lt] 和 arr[gt ... r] 再次分区 + * 这样对于大量重复元素就无需处理,可以优化不少性能消耗 + * + * 时间复杂度仍是 N*logN + * + * 三路快速排,无论是在大量重复元素的数据或者随机无序的大规模的数据或者近乎有序的大规模数据 + * 都能表现高效的处理优势. + */ +public class QuickSort3Ways { + + public static > void quickSort3Ways(T[] arr) { + assert arr != null; + int len = arr.length; + quickSort3Ways(arr,0,len-1); + } + + + public static > void quickSort3Ways(T[] arr, int l, int r) { + //优化 + if( r-l < 16){ + InsertionSort.sort(arr,l,r); + return; + } + +// if( l >= r){ +// return; +// } + + //partition + + //随机选取一个基准点并与最左侧位置的元素交换 + QuickSort.swap(arr,l,(int)(Math.random()*(r - l))+l); + + //获取基准点 + T e = arr[l]; + + 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 + //由于gt不断减少,gt右边是已分区好的元素,所以i 0){ + QuickSort.swap(arr,i,gt-1); + gt--; + }else {// ==v 的情况不用操作 + i++; + } + } + //交换基准的位置 + QuickSort.swap(arr,l,lt); + + //递归进行三路快排 + quickSort3Ways(arr,l,lt-1); + quickSort3Ways(arr,gt,r); + + } + + + + public static void main(String[] args) { +// Integer[] arr = {10,9,8,7,6,5,4,3,2,1}; +// QuickSort3Ways.quickSort3Ways(arr); +// for( int i = 0 ; i < arr.length ; i ++ ){ +// System.out.print(arr[i]); +// System.out.print(' '); +// } +// System.out.println(); + + //对于近乎有序的数组,优化后的归并排序效率更高 + int N = 200000; + Integer[] arr1 = SortTestHelper.generateNearlyOrderedArray(N, 4); + Integer[] arr2 = Arrays.copyOf(arr1, arr1.length); + Integer[] arr3 = Arrays.copyOf(arr1, arr1.length); + Integer[] arr4 = Arrays.copyOf(arr1, arr1.length); + System.out.println("处理近乎有序的数组:"); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.MergeSort","sortOptimize",arr1); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort","sort",arr2); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort2Ways","quickSort2Ways",arr3); + SortTestHelper.testSort("com.zejian.structures.Sort.Sort_NLogN.QuickSort3Ways","quickSort3Ways",arr4); + + + + } +} diff --git a/src/com/zejian/structures/Sort/Sort_N_2/InsertionSort.java b/src/com/zejian/structures/Sort/Sort_N_2/InsertionSort.java index 7d1423a..ee79b4d 100644 --- a/src/com/zejian/structures/Sort/Sort_N_2/InsertionSort.java +++ b/src/com/zejian/structures/Sort/Sort_N_2/InsertionSort.java @@ -74,23 +74,35 @@ public static > void sort(T[] array){ } - public static > void sort(T[] array,int l , int r){ - assert array != null; +// public static > void sort(T[] array,int l , int r){ +// assert array != null; +// +// //从第2个元素开始 +// for (int i = l+1; i <=r ; i++) { +// T e = array[i];//先记录要比较的元素,确定其交换的位置后再移动到对应位置 +// int j;//记录j,最后交换时使用 +// //更优雅的写法 +// //(array[j-1].compareTo(e) > 0)这个比较确保了发现前面没有更大元素就停止,这点与选择排序不同. +// for (j = i; j > l && array[j-1].compareTo(e) > 0; j--) { +// array[j] = array[j-1]; +// } +// array[j] = e; +// } +// } - //从第2个元素开始 - for (int i = l+1; i <=r ; i++) { - T e = array[i];//先记录要比较的元素,确定其交换的位置后再移动到对应位置 - int j;//记录j,最后交换时使用 - //更优雅的写法 - //(array[j-1].compareTo(e) > 0)这个比较确保了发现前面没有更大元素就停止,这点与选择排序不同. - for (j = i; j > l && array[j-1].compareTo(e) > 0; j--) { - array[j] = array[j-1]; - } - array[j] = e; + + // 对arr[l...r]的区间使用InsertionSort排序 + public static > void sort(T[] arr, int l, int r){ + + for( int i = l + 1 ; i <= r ; i ++ ){ + T e = arr[i]; + int j = i; + for( ; j > l && arr[j-1].compareTo(e) > 0 ; j--) + arr[j] = arr[j-1]; + arr[j] = e; } } - private static > void swap(T[] arr, int i, int j) { T t = arr[i]; arr[i] = arr[j]; From 61e89714205a1010d9c924b459bd1bb0942ad1e2 Mon Sep 17 00:00:00 2001 From: yumengtao Date: Tue, 13 Mar 2018 21:32:09 +0800 Subject: [PATCH 5/6] =?UTF-8?q?Prim=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/zejian/structures/Sort/Sort_NLogN/QuickSort.java | 2 +- .../zejian/structures/Sort/Sort_NLogN/QuickSort2Ways.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort.java b/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort.java index c5ef77b..79495ee 100644 --- a/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort.java +++ b/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort.java @@ -19,7 +19,7 @@ * 时间复杂度分析: * 快速排序每次将待排序数组分为两个部分,在理想状况下, * 每一次都将待排序数组划分成等长两个部分,则需要logN次划分 - * 即存在N层,而每层需要处理的元素个数都是一样的即N,此时时间 + * 即存在 logN 层,而每层需要处理的元素个数都是一样的即N,此时时间 * 复杂度为 N*logN * 而在最坏情况下,即数组已经有序或大致有序的情况下,每次划分 * 只能减少一个元素,快速排序将不幸退化为冒泡排序,因为每次分 diff --git a/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort2Ways.java b/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort2Ways.java index 2b82fcb..b58d184 100644 --- a/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort2Ways.java +++ b/src/com/zejian/structures/Sort/Sort_NLogN/QuickSort2Ways.java @@ -43,9 +43,9 @@ private static > void quickSort2Ways(T[] arr , int l , i InsertionSort.sort(arr,l,r); return; } - if( l >= r){ - return; - } +// if( l >= r){ +// return; +// } //计算并获取基准点的下标 int p = partition(arr,l,r); From ed95f46440453c1203b1ef17709c0ff1321a0f8e Mon Sep 17 00:00:00 2001 From: shinezejian Date: Thu, 21 Nov 2019 20:09:16 +0800 Subject: [PATCH 6/6] Update SingleILinkedList.java fixbug --- .../singleLinked/SingleILinkedList.java | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/src/com/zejian/structures/LinkedList/singleLinked/SingleILinkedList.java b/src/com/zejian/structures/LinkedList/singleLinked/SingleILinkedList.java index f780483..82e0f6f 100644 --- a/src/com/zejian/structures/LinkedList/singleLinked/SingleILinkedList.java +++ b/src/com/zejian/structures/LinkedList/singleLinked/SingleILinkedList.java @@ -218,36 +218,31 @@ public T remove(int index) { @Override public boolean removeAll(T data) { - boolean isRemove=false; + boolean isRemove = false; - if(this.head!=null&&data!=null){ - - //如果移除的是头结点 - if(data.equals(this.head.data)){ - this.head=this.head.next; - isRemove=true; + if(this.head != null && data != null){ + //如果移除的是头节点 + if(data.equals(this.head.data)){ + this.head = this.head.next; + isRemove = true; + } + + Node front = this.head; + Node pre = front.next; + //查找所有数据相同的节点并删除 + while(pre != null){ + if(data.equals(pre.data)){ + front.next = pre.next; + pre = front.next; + isRemove = true; }else { - - Node front=this.head; - Node pre=front.next; - //查找所有数据相同的结点并删除 - while (pre!=null){ - - if(data.equals(pre.data)){ - //更改指针指向 - front.next=pre.next; - pre =front.next; - isRemove=true; - }else { - front=pre; - pre=pre.next; - } - } + front = pre; + pre = pre.next; } - }else {//data=null || this.head=null - isRemove=false; } - return isRemove; + + } + return isRemove; } /**