冒泡排序
- 原理
冒泡排序是一个简单的排序算法, 它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来,如果没有可交换的元素,则排序完成 - 算法过程
1) 从序列首部开始,比较每对相邻元素,如果它们的顺序与要排序的顺序相反,则交换这连个数的位置
2) 1执行完后,最大或最小的元素会移动到序列尾部,然后再次从序列首部开始,执行1过程
3) 当没有元素需要交换位置是,排序结束 - 算法图解
- java代码实现
public class BubbleSort {
public static void bubbleSort(int[] arr) {
if (arr == null || arr.length < 2){
return;
}
boolean flag;
for (int i = 1; i < arr.length; i++){
flag = false;
for (int j =0; j< arr.length - i; j++){
// 相邻两个数,前面比后面大,则交换位置
if (arr[j] > arr[j+1]){
flag = true;
swap(arr, j, j+1);
}
}
if (!flag){
// 如果没有元素被交换,则代表已经排好序,结束循环
break;
}
}
}
public static void bubbleSort2(int[] arr){
if (arr == null || arr.length<2){
return;
}
boolean flag;
for(int e = arr.length-1; e>0 ; e--){
flag = false;
for(int j=0; j<e; j++){
if(arr[j] > arr[j+1]){
flag=true;
swap(arr, j, j+1);
}
}
if(!flag){
break;
}
}
}
public static void swap(int[] arr, int i, int j){
// 用异或操作实现两个数值的交换
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
public static void main(String[] args){
int[] arr_test = {5,2,6,3,2,7,2,4,7};
// bubbleSort(arr_test);
bubbleSort2(arr_test);
for (int j : arr_test) {
System.out.println(j);
}
}
}
- 冒泡排序的优化
设置一个flag变量,用来记录在一遍的扫描中,是否发生了元素位置的交换,如果没有发生,则代表已经排好序,没有必要再进行下一遍扫描,可以提前结束循环。
选择排序
-
原理
第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。 -
算法过程
- 第一趟:选出第一个元素后面所有元素的最小值,如果这个最小值比第一个元素小,则交换它们
- 第二趟:选出第二个元素后面所有元素的最小值,如果这个最小值比第二个元素小,则交换它们
- ······
- 第n-1趟:如果第n个元素比第n-1个元素小,则交换它们
- 算法图解
- java代码实现
public class SelectSort {
public static void selectSort(int[] arr){
if (arr==null || arr.length < 2){
return;
}
int min_num, index;
for (int i=1; i<arr.length; i++){
min_num = arr[i];
index = i;
for (int j=i; j<arr.length; j++){
if(arr[j]<min_num){
min_num = arr[j];
index = j;
}
}
if(min_num<arr[i-1]){
swap(arr, i-1, index);
}
}
}
}
插入排序
- 算法原理
它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
-
算法过程
1)从第一个元素开始,该元素可以认为已经被排序
2)取出下一个元素,如果它比已排序的元素序列的最后一个元素大,执行步骤3,否则从已排序的元素序列的最后向前扫描,寻找比第一个比它小的元素,将它插入这个元素的后面,如果没找到,则将它插到序列首部
3)取出已排序序列的下一个元素,重复步骤2
4)重复步骤2~3,直至完成排序 -
算法图解
-
java代码实现
public static void insetSort(int[] arr){
if(arr == null || arr.length < 2){
return;
}
for(int i = 1; i< arr.length; i++){
for(int j = i-1; j>=0; j--){
if(arr[j+1] < arr[j]){
swap(arr, j, j+1);
}
else{
break;
}
}
}
归并排序
-
算法原理
归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。 -
算法步骤
1)将待排序序列从中间等分成左右连个子序列
2)使用归并排序递归处理左序列
3)使用归并排序递归处理右序列
4)将左右两个有序子序列合并成一个完成的有序序列 -
算法图解
-
java代码实现, 递归实现
public class MergeSort {
public static void merge(int[] arr, int L, int M, int R){
int idx1 = L;
int idx2 = M + 1;
int i = 0;
int[] help = new int[R - L + 1];
while(idx1 <= M && idx2 <= R){
if(arr[idx1] <= arr[idx2]){
help[i] = arr[idx1];
idx1++;
}else{
help[i] = arr[idx2];
idx2++;
}
i++;
}
while(idx1<=M){
help[i++] = arr[idx1++];
}
while(idx2<=R){
help[i++] = arr[idx2++];
}
for(i = 0; i < help.length; i++){
arr[L+i] = help[i];
}
}
public static void process(int[] arr, int L, int R){
if(L == R){
return;
}
int mid = L + ((R - L) >> 1);
process(arr, L, mid);
process(arr, mid+1, R);
merge(arr, L, mid, R);
}
public static void mergeSort(int[] arr){
if(arr==null || arr.length<2){
return;
}
process(arr, 0, arr.length-1);
}
public static void main(String[] args){
int[] arr_test = {5,2,6,3,2,7,2,4,7};
mergeSort(arr_test);
for (int j : arr_test) {
System.out.println(j);
}
}
}
- java代码,非递归实现
// 非递归实现
public static void mergeSort2(int[] arr){
int mergeSize = 1;
int N = arr.length;
while(mergeSize < N){
int L = 0;
while(L < N) {
int M = L + mergeSize - 1;
// 如果剩余的数不够组成一个左组,则跳出循环
if (M > N - 1) {
break;
}
int R = M + Math.min(mergeSize, N - M - 1);
merge(arr, L, M, R);
L = R + 1;
}
if (mergeSize > N / 2){
// 防止数组长度过大,出现溢出
break;
}
mergeSize = mergeSize << 1;
}
}
快排
- partition过程
问题描述:
给定给定一个数组和一个数num,把小于等于num的数放到数组的左边,大于num的数放到数组右边。要求额外空间复杂度为O(1),时间复杂度为O(n)。
步骤:
- 划定一个小于等于区域,初始时小于等于区右边界在-1位置,i=0
- 然后比较arr[i]与num的大小,如果arr[i] <= num, 则arr[i]和小于等于区下一个位置交换,然后小于等于区右扩一位,i++;
- 如果arr[i] > num, 则 i++
- 荷兰国旗问题
问题描述:
给定一个数组和一个数num,将小于num的数放左边,等于num的数放中间,大于num的数放右边。这是partition问题的升级版。
步骤:
相比一般的partition问题,这里需要一个小于区和一个大于区。
- 划定一个小于区和一个大于区,初始时,小于区右边界在-1位置,大于区有边界在数组的第一个越界位置,并且i=0。
- 比较arr[i]和num的大小,arr[i] 等于num,则不进行任何操作,i++
- arr[i] 小于num,则arr[i]和小于区的右一个交换,小于区有阔1,i++
- arr[i] 大于num,则arr[i]和大于区的左一个交换,大于区左阔1,i不变。这里i不变的原因是从右边交换过来的数还没被检查过。
- 直到 i 等于大于区的边界,算法结束。
- 快排的java实现
public class QuickSort {
public static int partition(int[] arr, int L, int R){
//这里的L和R是数组的index
if (L > R){
return -1;
}
if (L == R){
return L;
}
int lessEqual = L - 1;
int i = L;
while (i < R ){
if(arr[i] <= arr[R]){
swap(arr, lessEqual+1, i);
lessEqual++;
}
i++;
}
swap(arr, ++lessEqual, R);
return lessEqual; //返回小于等于区的边界位置, 返回的这个位置的元素一定是arr[R]
}
public static void swap(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static int[] netherlandsFlag(int[] arr, int L, int R){
//返回等于区的左右两个边界位置,以arr[R]作为划分元素
if(L > R){
return new int[] {-1, -1};
}
if(L == R){
return new int[] {L, L};
}
int less = L - 1;
int more = R;
int i = L;
while(i < more){
if(arr[i] == arr[R]){
i++;
}else if(arr[i] < arr[R]) {
swap(arr, i, less + 1);
i++;
less++;
}else{
swap(arr, i, more - 1);
more--;
}
}
swap(arr, R, more);
return new int[] {less+1, more};
}
public static void process1(int[] arr, int L, int R){
// 快排1.0版本,使用partition,和arr[R]作为分界点
if(L > R){
return;
}
int M = partition(arr, L, R);
process1(arr, L, M-1); // 左侧范围
process1(arr, M+1, R); // 右侧范围
}
public static void process2(int[] arr, int L, int R){
// 快排2.0版本,使用netherlandsFlag,和arr[R]作为分界点
if(L>R){
return;
}
int [] equalArea = netherlandsFlag(arr, L, R);
process2(arr,0, equalArea[0]-1);
process2(arr, equalArea[1]+1, R);
}
public static void process3(int[] arr, int L, int R){
// 随机快排,随机选取一个元素作为分割点,时间复杂度最低。
if(L>R){
return;
}
swap(arr, L + (int)(Math.random() * (R - L + 1 )), R);
int [] equalArea = netherlandsFlag(arr, L, R);
process3(arr,0, equalArea[0]-1);
process3(arr, equalArea[1]+1, R);
}
public static void quickSort1(int[] arr){
if(arr == null|| arr.length < 2){
return;
}
process1(arr, 0, arr.length-1);
}
public static void quickSort2(int[] arr){
if(arr == null|| arr.length < 2){
return;
}
process2(arr, 0, arr.length-1);
}
// 随机快排,最经典的快排
public static void quickSort3(int[] arr){
if(arr == null|| arr.length < 2){
return;
}
process3(arr, 0, arr.length-1);
}
public static void main(String[] args){
int[] arr_test = {5,2,6,3,2,7,2,4,7,3,8,89};
quickSort3(arr_test);
for (int j : arr_test) {
System.out.println(j);
}
}
}