说明:从大一的实验室纳新需要了解排序算法到现在大三,期间还学了数据结构,详细讲了排序算法,但到了现在大三一提起排序,唯一有印象的还是冒泡排序,所以想抽点时间整合一下各类排序算法,差不多有14种算法,我用了比较粗略的方法计算每种排序算法分别在大数组和小数组的情况下的运行时间,并且给出了每种排序算法的时空复杂度。代码放在最下面
1:插入类排序
a:直接插入排序:
public void sort(int[] r){
int i, j, t ;
/*t = a ;
* a = b ;
* b = t ;*/
for(i=1; i<r.length; i++){
if(r[i] < r[i-1]){
//设置监视哨,其作用是一个暂存值的变量(相当于交换两个值的中间变量)并监视r[i]的值,相当于t = a
t = r[i] ;
//r[i]是当前需要插入的值
//相当于a = b,从插入的位置统一向后移一位,给b腾一个地方
for(j=i-1; j>=0 && t<r[j]; j--){
r[j+1] = r[j] ;
}
//相当于b = t
r[j+1] = t ;
}
}
}
测试运行:
小数组:
大数组:
b.折半插入排序:数组从0开始
int i, j, t, low, high, mid ;
for(i=1; i<r.length; i++){
if(r[i] < r[i-1]) {
t = r[i];
low = 0;
high = i - 1;
while (low <= high) { //寻找插入位置
mid = (low + high) / 2;
if (r[i] < mid)
high = mid - 1;
else if (r[i] == mid) { //找到插入位置了
low = mid + 1;
break;
}
else low = mid + 1;
}
for(j=i-1; j>=low; j--){ //从找到的low位置向后移一位,覆盖掉r[i]
r[j+1] = r[j] ;
}
r[j+1] = t ;
}
}
测试运行:
大数组:
c.希尔排序:
public String shellSort(int[] r, int[] rlta){ //rlta是增量序列,存放增量大小
String put = "" ;
int i ;
for (i=0; i<rlta.length; i++)
r = shellInsert(r, rlta[i]);
for (i=0; i<r.length; i++)
put += r[i] + " " ;
put += "\n" ;
return put ;
}
public int[] shellInsert(int[] r, int dk){ //dk代表增量的大小
int i, j, t ;
for (i=0+dk; i<r.length; i++){
if (r[i] < r[i-dk]){
t = r[i] ;
//r[j]如果大于t说明逆序,需要后移
for (j=i-dk; j>=0 && r[j]>t; j-=dk){
r[j+dk] = r[j] ;
}
r[j+dk] = t ;
System.out.println(t);
}
}
return r ;
}
测试运行:
小数组:
大数组:
2.交换类排序:
a.冒泡排序:
for (i=0; i<r.length; i++){
for (j=0; j<r.length-i-1; j++){
if(r[j] > r[j+1]){
t = r[j] ;
r[j] = r[j+1] ;
r[j+1] = t ;
}
}
}
测试运行:
小数组:
大数组:
b.快速排序:
public int QKpass(int[] r, int low, int high){ //一趟快速排序
int t ;
t = r[low] ; //设置枢轴
while (low < high){
while (low < high && r[high] >= t) //将小于t的移到左边
high-- ;
r[low] = r[high] ;
while (low < high && r[low] <= t) //将大于t的移到右边
low++ ;
r[high] = r[low] ;
}
r[low] = t ;
return low ; //返回分割断点
}
public void QKsort(int r[], int low, int high){
int pos ;
if(low < high) {
pos = QKpass(r, low, high);
QKsort(r, low, pos - 1); //为啥不管pos?因为pos里的值已经和左右序列比较过了
QKsort(r, pos + 1, high);
}
}
测试运行:
小数组:
大数组:
3.选择类排序:
a.简单选择排序:
for (i=0; i<r.length; i++){
k = i ;
//找最小值的下标
for (j=i+1; j<r.length; j++){
if (r[j] <= r[k]){
k = j ;
}
}
if (k != i){
t = r[k] ;
r[k] = r[i] ;
r[i] = t ;
}
}
测试运行:
小数组:
大数组:
b.树形选择排序(锦标赛排序):
public int[] TreeSelectionSort(int[] r)
{
int MinValue = -10000 ; //极值
int[]tree = new int[r.length * 4] ; //树的大小(非真实长度)
int baseSize = 1 ; //求树的长度用的辅助变量
int n = r.length ; //待排数的个数
int max ; //记录树结构中最大值的变量
int maxIndex ; //记录树结构中最大值的下标变化的变量
int treeSize ; //树的真实长度
int i ;
while (baseSize < n) { //求叶子节点为n的完全二叉树的节点个数
baseSize *= 2 ;
}
treeSize = baseSize * 2 - 1 ;
//从后往前存放叶子节点
for (i = 0; i < n; i++) {
tree[treeSize - i] = r[i] ;
}
//此时i=n,给剩余的一个叶子节点赋一个空值
for (; i < baseSize; i++) {
tree[treeSize - i] = MinValue ;
}
//构造树的非叶子节点,0位数组不用
for (i = treeSize; i > 1; i -= 2) {
tree[i / 2] = (tree[i] > tree[i - 1] ? tree[i] : tree[i - 1]) ;
}
n -= 1 ;
while (n != -1)
{
max = tree[1] ;
r[n--] = max ;
maxIndex = treeSize ;
//寻找叶子节点中最大值的数组下标
while (tree[maxIndex] != max) {
maxIndex-- ;
}
tree[maxIndex] = MinValue ; //赋极小值
while (maxIndex > 1) {
if (maxIndex % 2 == 0) { //是左节点,为啥要分左右?因为maxIndex奇偶性不确定
tree[maxIndex / 2] = (tree[maxIndex] > tree[maxIndex + 1] ? tree[maxIndex] : tree[maxIndex + 1]) ;
}
else {
tree[maxIndex / 2] = (tree[maxIndex] > tree[maxIndex - 1] ? tree[maxIndex] : tree[maxIndex - 1]) ;
}
maxIndex /= 2 ;
}
}
return r ;
}
测试运行:
小数组:
大数组:
c.堆排序:
public int[] heapSort(int[] r){
int i ;
//这里元素的索引是从0开始的,所以最后一个非叶子结点r.length/2 - 1,若是1开始,则为r.length/2
//第一次建堆开始
//这里是经过优化的,如果从叶子结点r.length-1开始建堆也是可以的
for (i = r.length / 2 - 1; i >= 0; i--) {
//i指向第一个非叶子节点
adjustHeap(r, i, r.length);
}
// 下面,开始后续的排序和建堆
for (int j = r.length - 1; j > 0; j--) {
//将建好堆的根节点(最大值)放到队尾,完成一次排序
swap(r, 0, j);
//再次建堆,除去已经排好序的末尾节点
adjustHeap(r, 0, j);
}
return r ;
}
/**
* 整个堆排序最关键的地方,对以i为根节点的树建大顶堆
* @param r 待组堆
* @param i 为以i为根节点的树建堆
* @param length 堆的长度
*/
public static void adjustHeap(int[] r, int i, int length) {
// 先把当前元素取出来,当前元素需要一直从根比较到叶子结点
int temp = r[i];
for (int k = 2 * i + 1; k < length; k = 2 * k + 1) { //2*i+1为左子树i的左子树(因为i是从0开始的),2*k+1为k的左子树
// 让k先指向子节点中最大的节点
if (k + 1 < length && r[k] < r[k + 1]) { //如果有右子树,并且右子树大于左子树
k++;
}
//如果发现结点(左右子结点)大于根结点,则进行值的交换
if (r[k] > temp) {
swap(r, i, k);
// 如果子节点更换了,那么,以子节点为根的子树会受到影响,所以,循环对子节点所在的树继续进行判断
i = k;
} else { //不用交换,直接终止循环
break;
}
}
}
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
测试用例:
小数组:
大数组:
4.归并类排序:
a.二路归并排序:
代码:排序算法汇总<-点这里