Skip to content

Commit c62940b

Browse files
committed
八大排序算法实现(直接插入排序,希尔排序,直接选择排序,堆排序,冒泡排序,快速排序,归并排序,基数排序)
1 parent 42bbd05 commit c62940b

10 files changed

Lines changed: 500 additions & 12 deletions

src/cn/edu/tju/rico/queue/OptimizationStackQueue.java

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
import cn.edu.tju.rico.stack.Node;
55

66
/**
7-
* Title: 使用两个栈模拟一个队列
8-
* Description: 其中一个栈专门用作入队(始终不执行出队操作),另一个栈专门用作出队(始终不执行入队操作)
9-
* 此种解法相对于StackQueue性能要高不少,避免了反复“倒”栈,仅在需要时才“倒”一次!!!
7+
* Title: 使用两个栈模拟一个队列 Description: 其中一个栈专门用作入队(始终不执行出队操作),另一个栈专门用作出队(始终不执行入队操作)
8+
* 此种解法相对于StackQueue性能要高不少,避免了反复“倒”栈,仅在需要时才“倒”一次!!!
9+
*
1010
* @author rico
1111
* @created 2017年5月19日 下午10:45:11
1212
*/
@@ -16,6 +16,7 @@ public class OptimizationStackQueue<E> {
1616
private LinkedStack<E> stack2; // 出队栈
1717

1818
public OptimizationStackQueue() {
19+
1920
stack1 = new LinkedStack<E>();
2021
stack2 = new LinkedStack<E>();
2122
}
@@ -32,29 +33,29 @@ public void put(E e) {
3233

3334
/**
3435
* @description 删除队头并返回队头元素的值。先检查stack2是否为空:
35-
* 若为空,先将stack1中的元素倒回stack2(stack1元素不弹出!),再对stack2执行弹栈操作
36+
* 若为空,先将stack1中的元素全部倒回stack2,再对stack2执行弹栈操作
3637
* 否则,则直接对stack2执行弹栈操作
3738
* @author rico
3839
* @created 2017年5月19日 下午10:48:32
3940
* @return
4041
*/
4142
public E pop() {
4243
if (stack2.isEmpty()) {
43-
Node<E> temp = stack1.peek(); // Stack1始终不执行弹栈操作,peek:获取元素但不删除
44-
while (temp != null) {
45-
stack2.push(temp.getData());
46-
temp = temp.getNext();
44+
while (!stack1.isEmpty()) {
45+
stack2.push(stack1.pop().getData());
4746
}
4847
}
4948
return stack2.pop().getData();
5049
}
5150

5251
@Override
5352
public String toString() {
54-
String str = new StringBuilder(stack1.toString()).reverse().toString();
55-
if(stack2.isEmpty()){
56-
return str;
53+
StringBuilder sb = new StringBuilder();
54+
if (!stack2.isEmpty()) {
55+
sb.append(stack2.toString());
5756
}
58-
return str.substring(str.indexOf(stack2.peek().getData().toString()));
57+
return sb.append(
58+
new StringBuilder(stack1.toString()).reverse().toString())
59+
.toString();
5960
}
6061
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package cn.edu.tju.rico.sort;
2+
3+
import java.util.Arrays;
4+
5+
/**
6+
* Title: 交换排序中的冒泡排序 ,一般情形下指的是优化后的冒泡排序,最多进行n-1次比较
7+
* Description:因为越大的元素会经由交换慢慢"浮"到数列的顶端(最后位置),最大的数最后才确定下来,所以称为冒泡排序
8+
* 时间复杂度:最好情形O(n),平均情形O(n^2),最差情形O(n^2)
9+
* 空间复杂度:O(1)
10+
* 稳 定 性:稳定
11+
* 内部排序(在排序过程中数据元素完全在内存)
12+
*
13+
* @author rico
14+
* @created 2017年5月20日 上午10:40:00
15+
*/
16+
public class BubbleSort {
17+
18+
19+
/**
20+
* @description 朴素冒泡排序(共进行n-1次比较)
21+
* @author rico
22+
*/
23+
public static int[] bubbleSort(int[] target) {
24+
int n = target.length;
25+
if (target != null && n != 1) {
26+
// 最多需要进行n-1躺,每一趟将比较小的元素移到前面,比较大的元素自然就逐渐沉到最后面了,这就是冒泡
27+
for (int i = 0; i < n-1; i++) {
28+
for (int j = n-1; j > i; j--) {
29+
if(target[j] <target[j-1]){
30+
int temp = target[j];
31+
target[j] = target[j-1];
32+
target[j-1] = temp;
33+
}
34+
}
35+
System.out.println(Arrays.toString(target));
36+
}
37+
}
38+
return target;
39+
}
40+
41+
/**
42+
* @description 优化冒泡排序
43+
* @author rico
44+
*/
45+
public static int[] optimizeBubbleSort(int[] target) {
46+
int n = target.length;
47+
if (target != null && n != 1) {
48+
// 最多需要进行n-1躺,每一趟将比较小的元素移到前面,比较大的元素自然就逐渐沉到最后面了,这就是冒泡
49+
for (int i = 0; i < n-1; i++) {
50+
boolean exchange = false;
51+
for (int j = n-1; j > i; j--) {
52+
if(target[j] < target[j-1]){
53+
int temp = target[j];
54+
target[j] = target[j-1];
55+
target[j-1] = temp;
56+
exchange = true;
57+
}
58+
}
59+
System.out.println(Arrays.toString(target));
60+
if (!exchange){
61+
return target;
62+
}
63+
}
64+
}
65+
return target;
66+
}
67+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package cn.edu.tju.rico.sort;
2+
3+
import java.util.Arrays;
4+
5+
/**
6+
* Title: 归并排序 ,概念上最为简单的排序算法,是一个递归算法
7+
* Description:归并排序包括两个过程:归 和 并
8+
* "归"是指将原序列分成半子序列,分别对子序列进行递归排序
9+
* "并"是指将排好序的各子序列合并成原序列
10+
*
11+
* 归并排序的主要问题是:需要一个与原待排序数组一样大的辅助数组空间
12+
*
13+
* 归并排序不依赖于原始序列,因此其最好情形、平均情形和最差情形时间复杂度都一样
14+
* 时间复杂度:最好情形O(n),平均情形O(n^2),最差情形O(n^2)
15+
* 空间复杂度:O(n)
16+
* 稳 定 性:稳定
17+
* 内部排序(在排序过程中数据元素完全在内存)
18+
*
19+
* @author rico
20+
* @created 2017年5月20日 上午10:40:00
21+
*/
22+
public class MergeSort {
23+
24+
/**
25+
* @description 归并排序算法(递归算法):递去分解,归来合并
26+
* @author rico
27+
* @created 2017年5月20日 下午4:04:52
28+
* @param target 待排序序列
29+
* @param left 待排序序列起始位置
30+
* @param right 待排序序列终止位置
31+
* @return
32+
*/
33+
public static int[] mergeSort(int[] target, int left, int right) {
34+
35+
if(right > left){ // 递归终止条件
36+
int mid = (left + right)/2;
37+
mergeSort(target, left, mid); // 归并排序第一个子序列
38+
mergeSort(target, mid+1, right); // 归并排序第二个子序列
39+
return merge(target,left,mid,right); // 合并子序列成原序列
40+
}
41+
return target;
42+
}
43+
44+
45+
/**
46+
* @description 两路归并算法
47+
* @author rico
48+
* @created 2017年5月20日 下午3:59:16
49+
* @param target 用于存储归并结果
50+
* @param left 第一个有序表的第一个元素所在位置
51+
* @param mid 第一个有序表的最后一个元素所在位置
52+
* @param right 第二个有序表的最后一个元素所在位置
53+
* @return
54+
*/
55+
public static int[] merge(int[] target, int left, int mid, int right){
56+
57+
// 需要一个与原待排序数组一样大的辅助数组空间
58+
int[] temp = Arrays.copyOf(target, target.length);
59+
60+
// s1,s2是检查指针,index 是存放指针
61+
int s1 = left;
62+
int s2 = mid + 1;
63+
int index = left;
64+
65+
// 两个表都未检查完,两两比较
66+
while(s1 <= mid && s2 <= right){
67+
if(temp[s1] <= temp[s2]){ // 稳定性
68+
target[index++] = temp[s1++];
69+
}else{
70+
target[index++] = temp[s2++];
71+
}
72+
}
73+
74+
//若第一个表未检查完,复制
75+
while(s1 <= mid){
76+
target[index++] = temp[s1++];
77+
}
78+
79+
//若第二个表未检查完,复制
80+
while(s2 <= right){
81+
target[index++] = temp[s2++];
82+
}
83+
return target;
84+
}
85+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package cn.edu.tju.rico.sort;
2+
3+
import java.util.Arrays;
4+
5+
/**
6+
* Title: 交换排序中的快速排序,目前应用最为广泛的排序算法,是一个递归算法
7+
* Description:归并排序包括两个过程:划分 和 快排
8+
* "划分"是指将原序列按基准元素划分两个子序列
9+
* "快排"是指分别对子序列进行快排
10+
*
11+
* 就平均计算时间而言,快速排序是所有内部排序方法中最好的一个
12+
*
13+
* 对大规模数据排序时,快排是快的;对小规模数据排序时,快排是慢的,甚至慢于简单选择排序等简单排序方法
14+
*
15+
* 归并排序依赖于原始序列,因此其时间复杂度从O(nlgn)到O(n^2)不等
16+
* 时间复杂度:最好情形O(nlgn),平均情形O(nlgn),最差情形O(n^2)
17+
*
18+
* 递归所消耗的栈空间
19+
* 空间复杂度:O(lgn)
20+
*
21+
* 可选任一元素作为基准元素
22+
* 稳 定 性:不稳定
23+
*
24+
*
25+
* 内部排序(在排序过程中数据元素完全在内存)
26+
*
27+
* @author rico
28+
* @created 2017年5月20日 上午10:40:00
29+
*/
30+
public class QuickSort {
31+
32+
/**
33+
* @description 快排算法(递归算法):在递去过程中就把问题解决了
34+
* @author rico
35+
* @created 2017年5月20日 下午5:12:06
36+
* @param target
37+
* @param left
38+
* @param right
39+
* @return
40+
*/
41+
public static int[] quickSort(int[] target, int left, int right) {
42+
43+
if(right > left){ // 递归终止条件
44+
int base_index = partition(target,left, right); // 原序列划分后基准元素的位置
45+
quickSort(target, left, base_index-1); // 对第一个子序列快速排序,不包含基准元素!
46+
quickSort(target, base_index+1, right); // 对第二个子序列快速排序,不包含基准元素!
47+
return target;
48+
}
49+
return target;
50+
}
51+
52+
53+
/**
54+
* @description 序列划分,以第一个元素为基准元素
55+
* @author rico
56+
* @created 2017年5月20日 下午5:10:54
57+
* @param target 序列
58+
* @param left 序列左端
59+
* @param right 序列右端
60+
* @return
61+
*/
62+
public static int partition(int[] target, int left, int right){
63+
64+
int base = target[left]; // 基准元素的值
65+
int base_index = left; // 基准元素最终应该在的位置
66+
67+
for (int i = left+1; i <= right; i++) { // 从基准元素的下一个元素开始
68+
if(target[i] < base){ // 若其小于基准元素
69+
base_index++; // 若其小于基准元素,则基准元素最终位置后移;否则不用移动
70+
if(base_index != i){ // 相等情况意味着i之前的元素都小于base,只需要换一次即可,不需要次次都换
71+
int temp = target[base_index];
72+
target[base_index] = target[i];
73+
target[i] = temp;
74+
}
75+
}
76+
}
77+
78+
// 将基准元素就位
79+
target[left] = target[base_index];
80+
target[base_index] = base;
81+
82+
System.out.println(Arrays.toString(target));
83+
84+
return base_index; //返回划分后基准元素的位置
85+
}
86+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package cn.edu.tju.rico.sort;
2+
3+
/**
4+
* Title: 分配排序中的基数排序
5+
* Description: 不是在对元素进行比较的基础上进行排序,而是采用 "分配 + 收集" 的办法
6+
*
7+
* 首先,将目标序列各元素分配到各个桶中;
8+
* 其次,将各个桶中的元素按先进先出的顺序再放回去
9+
* 如此往复...
10+
*
11+
* 时间复杂度:O(d*(r+n))或者 O(dn),d 的大小一般会受到 n的影响
12+
* 空间复杂度:O(rd + n)或者 O(n)
13+
* 稳 定 性:稳定
14+
* 内部排序(在排序过程中数据元素完全在内存)
15+
* @author rico
16+
* @created 2017年5月20日 上午10:40:00
17+
*/
18+
public class RadixSort {
19+
20+
/**
21+
* @description 分配 + 收集
22+
* @author rico
23+
* @created 2017年5月21日 下午9:25:52
24+
* @param target 待排序数组
25+
* @param r 基数
26+
* @param d 元素的位数
27+
* @param n 待排序元素个数
28+
* @return
29+
*/
30+
public static int[] radixSort(int[] target, int r, int d, int n){
31+
if (target != null && target.length != 1 ) {
32+
33+
int[][] bucket = new int[r][n]; // 一共有基数r个桶,每个桶最多放n个元素
34+
int digit; // 获取元素对应位上的数字,即装入那个桶
35+
int divisor = 1; // 定义每一轮的除数,1, 10, 100, ...
36+
int[] count = new int[r]; // 统计每个桶中实际存放元素的个数
37+
38+
for (int i = 0; i < d; i++) { // d 位的元素,需要经过分配、收集d次即可完成排序
39+
40+
// 分配
41+
for (int ele : target) {
42+
digit = (ele/divisor) % 10; // 获取元素对应位上的数字(巧妙!!!)
43+
bucket[digit][count[digit]++] = ele; // 将元素放入对应桶,桶中元素数目加1
44+
}
45+
46+
// 收集
47+
int index = 0; // 目标数组的下标
48+
for (int j = 0; j < r; j++) {
49+
int k = 0; // 用于按照先进先出顺序获取桶中元素
50+
while(k < count[j]){
51+
target[index++] = bucket[j][k++]; // 按照先进先出依次取出桶中的元素
52+
}
53+
count[j] = 0; // 计数器归零
54+
}
55+
divisor *= 10; //用于获取元素对应位数字
56+
}
57+
}
58+
return target;
59+
}
60+
}

0 commit comments

Comments
 (0)