排序总结
排序 | 最差时间 | 平均时间 | 稳定度 | 空间 |
---|---|---|---|---|
冒泡排序 | o(n2) | o(n2) | 稳定 | o(1) |
选择排序 | o(n2) | o(n2) | 稳定 | o(1) |
二叉树排序 | o(n2) | o(nlogn) | 不一定 | o(n) |
插入排序 | o(n2) | o(n2) | 稳定 | o(1) |
快速排序 | o(n2) | o(nlogn) | 不稳定 | o(log2n)~o(n) |
堆排序 | o(nlogn) | o(nlogn) | 不稳定 | o(1) |
希尔排序 | o(n^s) 1 |
冒泡排序
这种方法的基本思想是,将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮。在冒泡排序算法中我们要对这个“气泡”序列处理若干遍。自底向上检查一遍这个序列,并时刻注意两个相邻的元素的顺序是否正确。如果发现两个相邻元素的顺序不对,即“轻”的元素在下面,就交换它们的位置。显然,处理一遍之后,“最轻”的元素就浮到了最高位置;处理二遍之后,“次轻”的元素就浮到了次高位置。
public static void bubble(int[] a) {
if (a.length <= 0) {
return;
}
for (int j = 0; j < a.length; j++) {
for (int i = a.length - 1; i > j; i--) {
if (a[i] < a[i - 1]) {
swap(a, i ,i - 1);
}
}
}
}
public static void swap(int[] a, int m, int n) {
if (m == n) {
return;
}
a[m] = a[m] + a[n];
a[n] = a[m] - a[n];
a[m] = a[m] - a[n];
}
选择排序
选择排序的基本思想是对待排序的记录序列进行n-1遍的处理,第i遍处理是将L[i..n]中最小者与L[i]交换位置。这样,经过i遍处理之后,前i个记录的位置已经是正确的了。
插入排序
插入排序的基本思想是,经过i-1遍处理后,L[1..i-1]己排好序。第i遍处理仅将L[i]插入L[1..i-1]的适当位置,使得L[1..i] 又是排好序的序列。
希尔排序
一张图解释:
图来自:http://www.cnblogs.com/Ph-one/p/7307267.html
/**
* 希尔排序 针对有序序列在插入时采用交换法
* @param arr
*/
public static void sort(int []arr){
//增量gap,并逐步缩小增量
for(int gap=arr.length/2;gap>0;gap/=2){
//从第gap个元素,逐个对其所在组进行直接插入排序操作
for(int i=gap;i<arr.length;i++){
int j = i;
while(j-gap>=0 && arr[j]<arr[j-gap]){
//插入排序采用交换法
swap(arr,j,j-gap);
j-=gap;
}
}
}
}
计数排序
public static int[] countSort(int[]a){
int b[] = new int[a.length];
int max = a[0], min = a[0];
for (int i:a) {
if (i > max) {
max=i;
}
if (i < min) {
min=i;
}
}//这里k的大小是要排序的数组中,元素大小的极值差+1
int k = max - min + 1;
int c[] = new int[k];
for(int i = 0; i < a.length; ++i) {
c[a[i] - min] += 1;//优化过的地方,减小了数组c的大小
}
for (int i = 1; i < c.length; ++i) {
c[i] = c[i] + c[i-1];
}
for (int i = a.length - 1; i >= 0; --i) {
b[--c[a[i] - min]] = a[i];//按存取的方式取出c的元素
}
return b;
}
归并排序
/*
* 归并排序,将两个(或以上)有序列表合并成一个新的有序表
* 即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列
*/
public static int[] sort(int[] nums, int low, int high) {
if (low < high) {
int mid = (low + high) / 2;
sort(nums, low, mid);
sort(nums, mid + 1, high);
merge(nums, low, mid, high);
}
return nums;
}
public static void merge(int[] nums, int low, int mid, int high) {
int p_left = low;
int p_right = mid + 1;
int p_final = low;
int[] tmp = new int[nums.length];
while (p_left <= mid && p_right <= high) {
if (nums[p_left] < nums[p_right]) {
tmp[p_final++] = nums[p_left++];
} else {
tmp[p_final++] = nums[p_right++];
}
}
while (p_left <= mid) {
tmp[p_final++] = nums[p_left++];
}
while (p_right <= high) {
tmp[p_final++] = nums[p_right++];
}
for (int i = low; i <= high; i++) {
nums[i] = tmp[i];
}
}
/*
* 简洁版
*/
public static void mergeSort(int[] a, int[] temp, int low, int high) {
if (low >= high) {
return;
}
int mid = (low + high) / 2;
mergeSort(a, temp, low, mid);
mergeSort(a, temp, mid + 1, high);
int p_left = low, p_right = mid + 1, p_final = low;
while (p_left <= mid || p_right <= high) {
if (p_left <= mid && (p_right > high || a[p_left] <= a[p_right])) {
temp[p_final++] = a[p_left++];
} else {
temp[p_final++] = a[p_right++];
}
}
for (int i = low; i <= high; i++) {
a[i] = temp[i];
}
}
归并迭代版
以往使用的归并排序都是利用“分而治之”的思想递归进行,也就是不断二分到最小之后递归地归并上来
然而,再引入一个数组就可以进行迭代法的归并排序
把一个数组A里的先两两排序到另一个数组B里
再把数组B里的数据四个四个排序到A里,以此类推
public static void mergeSort(int[] a){
int[] tmp = new int[a.length];//用于存放归并结果
int k=1;//起始,子序列长度为1
while(k<a.length){
mergePass(a, tmp, k, a.length);//将原先无序的数据两两归并入tmp
k = 2*k;//子序列长度加倍
mergePass(tmp, a, k, a.length);//将tmp中已经两两归并的有序序列再归并回数组a
k = 2*k;//子序列长度加倍
}
}
public static void mergePass(int[] SR, int [] TR,int s,int len){
int i=0;
while (i < len - 2 * s + 1) {
merge(SR, TR, i, i + s - 1, i + 2 * s - 1);//两两归并
i = i + 2 * s;
}
//处理最后的尾数
if(i < len - s + 1) {
merge(SR, TR, i, i + s - 1, len - 1);//归并最后两个序列
}else {
for (int j = i; j < len; j++) {//若最后只剩下单个子序列
TR[j] = SR[j];
}
}
}
// merge i-(m-1) and m-n
public static void merge(int[] s, int[] t,int i,int m,int n){
int j,k,l;
for(j = m + 1, k = i; i <= m && j <= n; k++){
if(s[i] < s[j]){
t[k] = s[i++];
}else{
t[k] = s[j++];
}
}
if(i <= m){
for (l = 0; l <= m-i ; l++) {
t[k+l] = s[i+l];
}
}
if(j<=n){
for (l = 0; l <= n-j; l++) {
t[k+l] = s[j+l];
}
}
}
快排
/*
* 算法导论版
*/
public void quicksort(int[] A, int p, int r) {
if (p < r) {
int q = partition(A, p, r);
System.out.println("输出q" + q);
quicksort(A, p, q - 1);
quicksort(A, q + 1, r);
}
}
//每次分区返回一个int值,保证其左全小于该值,其右全大于该值
public int partition(int[] A, int l, int r) {
int key = A[r]; // 保证所有i + 1左边的都比A[r]小,从i+1开始比A[r]大
int i = l - 1;
for (int j = l; j < r; j++) {
if (A[j] < key) {
i++;
swap(A, i, j);
}
}
//交换A[r]和A[i+1]
swap(A, r, i+1);
System.out.println("h " + Arrays.toString(A));
return i+1;
}
public static void swap(int[] A, int i, int j) {
if(i == j) return; // 注意一定要去掉!!!!
A[i] = A[i] + A[j];
A[j] = A[i] - A[j];
A[i] = A[i] - A[j];
}
/*
* 简洁
* 示例:453126
*/
public static void qSort(int[] a, int low, int high) {
if (low >= high) return;
int first = low, last = high, key = a[first];
while (first < last) {
while(first < last && a[last] >= key)
--last;
a[first] = a[last];
while(first < last && a[first] <= key)
++first;
a[last] = a[first];
}
a[first] = key;
qSort(a, low, first - 1);
qSort(a, last + 1, high);
}
快排递归和迭代版
迭代的性能高于递归:
递归会把中间结果压到栈中,而且每次调用函数都会将参数,局部变量压栈。导致大量地消耗栈空间,而且,每次函数调用还要将下一次指令执行的位置压栈,这些上下文(Context)切换会导致不少开销,甚至会导致栈溢出。
Java的方法栈深度只有几k,而java.util的Stack是基于数组实现的。
public static void qsort(int[] a, int s, int e) {
if (s >= e) {
return;
}
int l = s, r = e, key = a[e];
while (l < r) {
while (a[l] <= key && l < r) {
l++;
}
while (a[r] >= key && r > l) {
r--;
}
swap(a, l, r);
}
swap(a, l, e);
qsort(a, s, l - 1);
qsort(a, r + 1, e);
}
public static void qsortIteration(int[] a) {
Queue<Integer> q = new ArrayDeque<Integer>();
q.add(0);
q.add(a.length - 1);
while (q.size() != 0) {
int s = q.poll();
int e = q.poll();
int l = s, r = e, key = a[e];
while (l < r) {
while (a[l] <= key && l < r) {
l++;
}
while (a[r] >= key && r > l) {
r--;
}
swap(a, l, r);
}
if (l < e) {
swap(a, l, e);
if (s != (l - 1)) {
q.add(s);
q.add(l - 1);
}
if ((r + 1) != e) {
q.add(r + 1);
q.add(e);
}
}
}
}
public static void swap(int[] a, int i, int j) {
if (i == j) {
return;
}
a[i] = a[i] + a[j];
a[j] = a[i] - a[j];
a[i] = a[i] - a[j];
}
堆排序
public class Main {
public static void main(String[] args) {
int[] data=new int[] {5,3,6,2,1,9,4,8,7,10};
print(data);
heapsort(data);
System.out.println("排序后数组");
print(data);
}
public static void heapsort(int[] data) {
build(data);
for(int i=data.length -1;i>0;i--) {
swap(data,0,i);
creatMax(data,i,0);
}
}
//建一个最大堆
public static void build(int[] data) {
for(int i=(data.length-2)/2;i>=0;i--){//这里i=data.length/2也行
creatMax(data,data.length,i);
}
}
// heapsize表多少个堆元素存储在数组中
// 建堆时,只需要保证第一个数为最大。所以从倒数第二行的最后一个有子节点的数开始遍历
// 排序时,从第一位开始遍历,交换最大的,再调用creatMax找到剩下的最大值,遍历从data.length-1依次减小。保证最大的放在最后。
public static void creatMax(int[] data,int heapsize,int last) { //last=4
int l=last*2+1;
int r=last*2+2;
int largest=last;
if(l<heapsize&&data[l]>data[last]) largest=l;
if(r<heapsize&&data[r]>data[largest]) largest=r;
if(largest!=last){
swap(data,largest,last);
creatMax(data,heapsize,largest);
}
}
public static void swap(int[] data,int i,int j) {
if(i==j) return;
data[i]=data[i]+data[j];
data[j]=data[i]-data[j];
data[i]=data[i]-data[j];
}
//打印方法
public static void print(int[] data) {
for(int i=0;i<data.length;i++) {
System.out.print(data[i]+" ");
}
System.out.println();
}
}
整数数组,输出所有可能的子集(整数可能有重复)
递归和迭代
递归
public static void main(String[] args) {
int[] a = {1,2,3,5};
List<List<Integer>> res = new ArrayList<List<Integer>>();
List<Integer> tmp = new ArrayList<Integer>();
helper(a, 0, res, tmp);
System.out.println(res);
}
public static void helper(int[] a, int level, List<List<Integer>> res, List<Integer> tmp) {
if (level >= a.length) {
return;
}
for (int i = level; i< a.length; i++,level++) {
tmp.add(a[i]);
res.add(new ArrayList<Integer>(tmp));
helper(a, level + 1, res, tmp);
tmp.remove(tmp.size() - 1);
}
}
迭代
public static void main(String[] args) {
int[] a = {1,2,3,5};
List<List<Integer>> res = new ArrayList<List<Integer>>();
List<Integer> tmp = new ArrayList<Integer>();
helper(a,res, tmp);
System.out.println(res);
}
public static void helper(int[] a,List<List<Integer>> res, List<Integer> tmp) {
for (int i = 0; i < a.length; i++) {
List<List<Integer>> re = new ArrayList<List<Integer>>();
tmp = new ArrayList<Integer>();
tmp.add(a[i]);
re.add(new ArrayList<Integer>(tmp));
for (int j = i+1; j< a.length; j++) {
List<List<Integer>> toAdd = new ArrayList<List<Integer>>();
for (List<Integer> l : re) {
List<Integer> ll = new ArrayList<Integer>(l);
ll.add(a[j]);
toAdd.add(new ArrayList<Integer>(ll));
}
re.addAll(toAdd);
}
res.addAll(re);
}
}
第k大值
public static int helper(int[] nums, int k) {
if (k < 1 || nums == null || nums.length == 0) {
return 0;
}
return getK(nums, nums.length - k + 1, 0, nums.length - 1);
}
public static int getK(int[] nums,int k, int begin, int end) {
int p = nums[end];
int l = begin;
int r = end;
while (true) {
while (nums[l] < p && l < r) {
l++;
}
while (nums[r] >= p && r > l) {
r--;
}
if (l == r) {
break;
}
swap(nums, l, r);
}
swap(nums, l, end);
if (k == l + 1) {
return p;
} else if (k < l + 1) {
return getK(nums, k, begin, l - 1);
} else {
return getK(nums, k, l + 1, end);
}
}
public static void swap(int[] nums, int a, int b) {
int tmp = nums[a];
nums[a] = nums[b];
nums[b] = tmp;
}