冒泡排序:两两比较,大数冒泡
原理:
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
每一轮对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。第一轮结束,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤n-1轮,分别倒序排好倒数第二大、倒数第三大……的元素。直到没有任何一对数字需要比较。
形象排序:
原始数据: 6 2 8 5 1
第一轮: 2 6 5 1 |8
第二轮: 2 5 1 |6 8
第三轮: 2 1 |5 6 8
第四轮: 1 |2 5 6 8
1
2
3
4
5
具体解释:
第一轮中:
第一次:比较第一个和第二个元素(即6和2),发现6比2大,则交换6和2(目前数字排列为 2 6 8 5 1)
第二次:比较第二个和第三个元素(即6和8),发现6比8小,则保持原样(目前数字排列为 2 6 8 5 1)
第三次:比较第三个和第四个元素(即8和5),发现8比5大,则交换8和5(目前数字排列为 2 6 5 8 1)
第四次:比较第四个和第五个元素(即8和1),发现8比1大,则交换8和1(目前数字排列为 2 6 5 1 8)
最后:这样,第一轮就把最大的元素放到了最右边
最后一个元素已经有序,则第二轮不用比较最后一个元素和倒数第二个元素的大小(目前数字排列为 2 6 5 1 |8)
第二轮中:
第一次:比较第一个和第二个元素(即2和6),发现2比6小,则保持原样(目前数字排列为 2 6 5 1 |8)
第二次:比较第二个和第三个元素(即6和5),发现6比5大,则交换6和5(目前数字排列为 2 5 6 1 |8)
第三次:比较第三个和第四个元素(即6和1),发现6比1大,则交换6和1(目前数字排列为 2 5 1 6 |8)
最后:这样,第二轮就把倒数第二大的元素放到了倒数第二的位置
倒数两个元素已经有序,则第三轮不用比较倒数第三个元素和倒数第二个元素的大小(目前数字排列为2 5 1 |6 8)
第三轮中:
第一次:比较第一个和第二个元素(即2和5),发现2比5小,则保持原样(目前数字排列为 2 5 1 |6 8)
第二次:比较第二个和第三个元素(即5和1),发现5比1大,则交换5和1(目前数字排列为 2 1 5 |6 8)
最后:这样,第三轮就把倒数第三大的元素放到了倒数第三的位置
倒数三个元素已经有序,则第四轮不用比较倒数第四个元素和倒数第三个元素的大小(目前数字排列为2 1 |5 6 8)
第四轮中:
第一次:比较第一个和第二个元素(即2和1),发现2比1大,则交换2和1(目前数字排列为 1 2 |5 6 8)
最后:这样,第四轮就把倒数第四大的元素放到了倒数第四的位置,所有的元素就按升序排好了
升序:
public static void bubbleSort(int[] arr){ int lgn = arr.length; for (int i = 0; i < lgn - 1; i++) { for (int j = 0; j < lgn - 1 - i; j++) { if(arr[j] > arr[j + 1]){ int temp = arr[j + 1]; arr[j + 1] = arr[j]; arr[j] = temp; } } } }
降序:
...
选择排序:选择剩余元素中最小(最大)的元素放置到初始选择集合中(空)
1. 选择排序法
(如果不想看解释分析,直接往后拉看代码)
实质:
第一轮:通过对比数组中前一个元素和后一个元素的大小,先找到数组中最小的数字,并且记录它的下标。如果标记的下标不是第一个元素的下标,则交换两个元素。
第二轮:从第二个元素开始(因为第一个元素已经是第一轮排好的“有序”元素),对比数组中前一个元素和后一个元素的大小,遍历寻找数组中第二小的数字,并记录它的下标。如果标记的下标不是第二个元素的下标,则交换两个元素。
第三轮:从第三个元素开始(因为第一和第二个元素已经是第一、二轮排好的“有序”元素),对比数组中前一个元素和后一个元素的大小,遍历寻找数组中第三小的数字,并记录它的下标。如果标记的下标不是第三个元素的下标,则交换两个元素。
第四轮:从第四个元素开始(因为第一、第二、第三个元素已经是第一、二、三轮排好的“有序”元素),对比数组中前一个元素和后一个元素的大小,遍历寻找数组中第四小的数字,并记录它的下标。如果标记的下标不是第四个元素的下标,则交换两个元素。
第五轮:没有第五轮(因为一共就五个数,排好四个数,自然就全部排好了,再多排一轮浪费了)
形象排序:
原始数据: |6 2 8 5 1
第一轮: 1 |2 8 5 6
第二轮: 1 2 |8 5 6
第三轮: 1 2 5 |8 6
第四轮: 1 2 5 6 |8
1
2
3
4
5
具体解释:
先假设第一个数字6为最小的数字,那么记录它的下标(index=0)
第一轮中:
第一次:先比较6和2(即标记的数和后面一个元素比较),发现2更小,则记录2的下标(index=1)
第二次:比较2和8(标记的数和后面一个元素比较),发现还是2小,则下标还是2的下标不变(index=1)
第三次:比较2和5(标记的数和更后面的数比较),发现还是2小,则下标还是2的下标不变(index=1)
第四次:比较2和1(标记的数和更后面的数比较),发现1比2小,则标记改为1的下标(index=4)
最后:index并不等于第一个元素的下标(0),则交换第一个元素和被标记的元素
第一个元素已经有序,则从第二个元素开始(并且把标记index初始化为1,代表第二个元素2)
第二轮中:
第一次:先比较2和8,还是2小,则下标还是2的下标不变(index=1)
第二次:比较2和5,还是2小,则下标还是2的下标不变(index=1)
第三次:比较2和6,还是2小,则下标还是2的下标不变(index=1)
最后:index等于第二个元素的下标(1),则不用交换第二个元素和被标记的元素
第一、二个元素(1和2)已经有序,则从第三个元素开始(并且把标记index初始化为2,代表第三个元素8)
第三轮中:
第一次:先比较8和5,发现5比8小,则标记改为5的下标(index=3)
第二次:比较5和6,还是5小,则下标还是5的下标不变(index=3)
最后:index并不等于第三个元素的下标(2),则交换第三个元素和被标记的元素
第一、二、三个元素(1和2和5)已经有序,则从第四个元素开始(并且把标记index初始化为3,代表第四个元素8)
第四轮中:
第一次:先比较8和6,发现6比8小,则标记改为6的下标(index=4)
最后:index并不等于第四个元素的下标(3),则交换第四个元素和被标记的元素
public static void SelectionSortAsc(int[] arr){
int min = 0;
for (int i = 0; i < arr.length - 1; i++) {
min = i;
for (int j = i + 1; j < arr.length ; j++) {
if(arr[j] < arr[min]){
min = j;
}
}
if(min != i){
int tmp = arr[i];
arr[i] = arr[min];
arr[min] = tmp;
}
}
}
public static void SelectionSortDesc(int[] arr){
int max = 0;
for (int i = 0; i < arr.length - 1; i++) {
max = i;
for (int j = i + 1; j < arr.length ; j++) {
if(arr[j] > arr[max]){
max = j;
}
}
if(max != i){
int tmp = arr[i];
arr[i] = arr[max];
arr[max] = tmp;
}
}
}
插入排序:设定一个初始已排序的集合(一般选择一个元素),从剩余的集合中将各个元素以此插入到初始集合中的正确位置
原理:
插入排序法通过把数组中的元素插入到适当的位置来进行排序:
先假设第一个元素有序,则第一轮从第二个元素开始,作为待插入的数,往前依次比较,看往哪里插
第二轮把下一个元素(第三个)插入到其对应于已排序元素的排序位置
对于数组中的每个元素重复2步骤。即把第四个元素插入到适当位置,然后是第5个元素,等等。
形象排序:
原始数据: 6| 2 8 5 1
第一轮: 2 6| 8 5 1
第二轮: 2 6 8| 5 1
第三轮: 2 5 6 8| 1
第四轮: 1 2 5 6 8|
1
2
3
4
5
具体解释:
假设第一个元素6是有序的,并且定义待插入的数int inserter=a[i],和定义下标index=i-1,用此下标来让插入点与对应数比较
因为第一个数假设是有序的,则从第二个数开始作为待插入的数(inserter=a[1])
第一轮中:
第一次:把inserter与第一个元素比较(即2与6),发现2比6小,则把第一个元素后挪一个位置(目前数字排列为 6 6| 8 5 1)
最后:把inserter中保留的待插入的数插入到相应位置(目前数字排列为 2 6| 8 5 1)
前面两个元素已经有序,则第二轮把第三个元素插到有序元素中的适当位置,则实现前三个元素有序(目前数字排列为 2 6| 8 5 1)
第二轮中:(保存第三个元素inserter=a[2])
第一次:把inserter与第二个元素比较(即8与6),发现8比6大,则把第二个元素不做后挪(目前数字排列为 2 6 8| 5 1)
第二次:由于8比6大,所以肯定比2大,所以不需要再比了
最后:把inserter中保留的待插入的数插入到相应位置(对于本题,则还是原位置)(目前数字排列为 2 6 8| 5 1)
前面三个元素已经有序,则第三轮把第四个元素插到有序元素中的适当位置,则实现前四个元素有序(目前数字排列为 2 6 8| 5 1)
第三轮中:(保存第四个元素inserter=a[3])
第一次:把inserter与第三个元素比较(即5与8),发现5比8小,则把第三个元素后挪一个位置(目前数字排列为 2 6 8 8| 1)
第二次:把inserter与第二个元素比较(即5与6),发现5比6小,则把第二个元素后挪一个位置(目前数字排列为 2 6 6 8| 1)
第三次:把inserter与第一个元素比较(即5与2),发现5比2大,则把第一个元素不做后挪(目前数字排列为 2 6 6 8| 1)
最后:把inserter中保留的待插入的数插入到相应位置(目前数字排列为 2 5 6 8| 1)
前面四个元素已经有序,则第四轮把第五个元素插到有序元素中的适当位置,则实现前五个元素有序(目前数字排列为 2 5 6 8| 1)
第五轮中:(保存第五个元素inserter=a[4])
第一次:把inserter与第四个元素比较(即1与8),发现1比8小,则把第四个元素后挪一个位置(目前数字排列为 2 5 6 8 8|)
第二次:把inserter与第三个元素比较(即1与6),发现1比6小,则把第三个元素后挪一个位置(目前数字排列为 2 5 6 6 8|)
第三次:把inserter与第二个元素比较(即1与5),发现1比5小,则把第二个元素后挪一个位置(目前数字排列为 2 5 5 6 8|)
第四次:把inserter与第一个元素比较(即1与2),发现1比2小,则把第一个元素后挪一个位置(目前数字排列为 2 2 5 6 8|)
最后:把inserter中保留的待插入的数插入到相应位置(目前数字排列为 1 2 5 6 8|),完成排序
public static void insertionSort(int [] array){ for(int i = 1; i < array.length; i++){ int temp = array[i];//被标记的值或者说是当前需要插入的值 int j = i; //如果轮循值大于被标记值则往后移 while( j > 0 && temp < array[j - 1]){ array[j] = array[j - 1]; j-- ; } //将被标记值插入最终移出的空位置 array[j] = temp; } }
快速排序:选取锚点,划分小于,大于两个集合,递归处理子集合
原理:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
先假设第一个元素为轴值,自右向左找一个比轴值小的数交换,再自左向右找一个比轴值大的数交换,再重复自右向左找一个比轴值小的数交换,自左向右找一个比轴值大的数交换,直到轴值左边没有比其大的数存在,右边也没有比其小的数存在,则第一轮结束。原来的一组数据被划分为以轴值为界的两组新数据
第二轮:取上一轮轴值左边的一组新数据,重复1的操作;取上一轮轴值右边的一组新数据,重复1的操作,则把最初的一组数据分成了四部分,这样便产生一个递归的思想
一直重复操作,直到数据被分的不可再分为止。
形象排序:
原始数据: |6| 2 8 5 1
第一轮: |1| 2 5 | 6 |8|
第二轮: 1 ||2| 5 | 6 | 8
第三轮: 1 | 2 | 5 | 6 | 8
1
2
3
4
具体解释:
第一轮中:(先假设第一个元素6为轴值)
第一次:自右向左找一个比轴值(即6)小的数交换,正巧右边的第一个数就比6小,则交换6和1(目前数字排列为 1 2 8 5 6)
第二次:自左向右找一个比轴值(即6)大的数交换,左边第一个数为1,不比6大,则找左边第二个数;左边第二个数为2,不必6大,找左边第三个数;左边第三个数为8,比6大,则交换6和8(目前数字排列为 1 2 6 5 8)
第三次:再自右向左找一个比轴值(即6)小的数交换,右边第一个数为8,不比6小,则找右边第二个数;右边第二个数为5,比6小,则交换6和5(目前数字排列为 1 2 5 6 8)
第四次:再自左向右找一个比轴值(即6)大的数交换,左边第一个数为1,不比6大,则找左边第二个数;左边第二个数为2,不必6大,则找左边第三个数;左边第三个数为5,不比6大,则找左边第四个数,结果第四个书就是轴值本身,则一轮循环停止(目前数字排列为 1 2 5 6 8)
最后:这样,第一轮就把最初的一组元素{ 6 2 8 5 1 }分为两组元素{ 1 2 5 }和{ 8 }(6为轴值,经历这几次遍历,便已经固定其正确位置了,以后不需要再考虑这个元素)
第二轮中:
先考虑第一轮轴值(即6)左边的数据 { 1 2 5 }:
第二轮中左边新数据的第一轮:(先假设新数据的第一个元素1为新的轴值)自右向左找一个比轴值(即1)小的数交换,右边第一个数为5,不比1小,则找右边第二个数;右边第二个数为2,不比1小,则找右边第三个数,结果右边第三个数就是轴值本身,则循环停止(目前数字排列为 1 2 5 ),同样的循环已经固定轴值(即1)的位置
同时,轴值1的左边没有数据,即分到了不可再分的地步,那么递归结束,而轴值1的右边还有数据 { 2 5 },则继续确立新的轴值为2,再进行如上操作,直到分到不可以再分,则递归终止,最后可以确保第一轮的轴值(即6)左边的新数据 { 1 2 5 }每个都被固定,则左边数据的递归结束。
再考虑第一轮轴值(即6)右边的数据 { 8 }:
已经被分到不可再分,则它的位置就已经被确定了,右边数据的递归结束。
最终整组数据就排列完毕
public static void quikeSort(int[] arr, int low, int high) { int start = low; int end = high; int anchor = arr[low]; while (end > start) { //比较 anchor---end while (end > start && arr[end] >= anchor) { //从末尾向前寻找小于等于anchor的值 end--; } //交换位置 if (arr[end] <= anchor) { int temp = arr[end]; arr[end] = arr[start]; arr[start] = temp; } //比较start---anchor while (end > start && arr[start] <= anchor) {//从起始位置向后寻找大于等于anchor的值 start++; } //交换位置 if (arr[start] >= anchor) { int temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; } } //anchor位置确定。左边的元素值都小于anchor值,右边的值都大于anchor值,递归排序左右两侧排序 //左边元素。low---anchor值索引-1 if (start > low) { quikeSort(arr, low, start - 1); } //右边元素。从anchor值索引+1---high if (end < high) { quikeSort(arr, end + 1, high); } }
归并排序:中分两个结合,分别归并排序,然后合并两个有序结合;递归进行
public static void mergeSort(int[] data) { sort(data, 0, data.length - 1); } public static void sort(int[] data, int left, int right) { if (left >= right) return; // 找出中间索引 int center = (left + right) / 2; // 对左边数组进行递归 sort(data, left, center); // 对右边数组进行递归 sort(data, center + 1, right); // 合并 merge(data, left, center, right); } /** * 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序 * * @param data * 数组对象 * @param left * 左数组的第一个元素的索引 * @param center * 左数组的最后一个元素的索引,center+1是右数组第一个元素的索引 * @param right * 右数组最后一个元素的索引 */ public static void merge(int[] data, int left, int center, int right) { // 临时数组 int[] tmpArr = new int[data.length]; // 右数组第一个元素索引 int mid = center + 1; // third 记录临时数组的索引 int third = left; // 缓存左数组第一个元素的索引 int tmp = left; while (left <= center && mid <= right) { // 从两个数组中取出最小的放入临时数组 if (data[left] <= data[mid]) { tmpArr[third++] = data[left++]; } else { tmpArr[third++] = data[mid++]; } } // 剩余部分依次放入临时数组(实际上两个while只会执行其中一个) while (mid <= right) { tmpArr[third++] = data[mid++]; } while (left <= center) { tmpArr[third++] = data[left++]; } // 将临时数组中的内容拷贝回原数组中 // (原left-right范围的内容被复制回原数组) while (tmp <= right) { data[tmp] = tmpArr[tmp++]; } }
基数排序:逐位排序
//LSD public static void radixLSDSort(int[] arr){ //最高位数 int maxBit = getMaxBit(arr); //十个bulk 分别存放 每个位数 数字 相应的元素 如个位为3 则放入bulk[3] Queue<Integer>[] bulk = new Queue[10]; for (int i = 0; i < bulk.length; i++) { bulk[i] = new ArrayDeque(); } //分别处理不同位数 存放 处理 for (int i = 0; i < maxBit; i++) { for (int j = 0; j < arr.length; j++) { int ivalue = (int) (arr[j] % Math.pow(10, i + 1) / Math.pow(10, i)); //去相应位数上的数字作为 存入bulk index bulk[ivalue].offer(arr[j]); } //取出bulk中的元素 重新放入数组 并清除bulk中的元素。 int arrIndex = 0; for (int j = 0; j < bulk.length; j++) { while (bulk[j].size()>0){ arr[arrIndex++] = bulk[j].poll(); } } } } public static int getMaxBit(int[] arr){ int max = arr[0]; for (int i = 1; i < arr.length; i++) { if(arr[i] > max){ max = arr[i]; } } int b = 0; while (max > 0){ max /= 10; b++; } return b; } public static void radixMSDSort(int[] arr){ msdSort(arr, 0, arr.length, getMaxBit(arr)); } //MSD public static void msdSort(int[] arr, int left, int right, int maxBit){ //十个bulk 分别存放 每个位数 数字 相应的元素 如个位为3 则放入bulk[3] Queue<Integer>[] bulk = new Queue[10]; for (int i = 0; i < bulk.length; i++) { bulk[i] = new ArrayDeque(); } //依据最高位分别放入不同的bulk for (int j = left; j < right; j++) { int ivalue = (int) (arr[j] % Math.pow(10, maxBit) / Math.pow(10, maxBit - 1)); //去相应位数上的数字作为 存入bulk index bulk[ivalue].offer(arr[j]); } //取出bulk中的元素 重新放入数组 并清除bulk中的元素。记录bulk中queue大小大于1的数组索引 递归使用 List<Integer> count = new ArrayList<Integer>(); int arrIndex = left; for (int j = 0; j < bulk.length; j++) { int start = arrIndex; while (bulk[j].size()>0){ arr[arrIndex++] = bulk[j].poll(); } if(arrIndex - start > 1){ count.add(start); count.add(arrIndex); } } //递归最小位判断 int nextBit = maxBit - 1; if(nextBit > 0) { for (int i = 1; i < count.size(); i += 2) { msdSort(arr, count.get(i - 1), count.get(i), nextBit); } } }
shell排序:插入排序+步长改进
public static void shellSort(int[] arr){ int step = arr.length/2; while (step >= 1) { //步长为1时 包含所有数组序列 for (int i = 0; i < step; i++) { //步长为n则数组将分为n组分别排序 for (int j = step + i; j < arr.length; j += step) { //对跨步长每组元素进行插入排序 int temp = arr[j]; int preIndex = j - step; while (preIndex > -1 && temp < arr[preIndex]) { arr[preIndex + step] = arr[preIndex]; preIndex -= step; } arr[preIndex + step] = temp; System.out.println(" middle: " + Arrays.toString(arr)); } } step /= 2; //每次步长处理 } }
/**
* 改进形式
* @param arr
*/
public static void shellSort1(int[] arr){
int step = arr.length/2;
while (step >= 1) { //步长为1时 包含所有数组序列
for (int i = step; i < arr.length; i += step) { //对跨步长每组元素进行插入排序
int temp = arr[i];
int j = i;
while (j > 0 && temp < arr[j - step]) {
arr[j] = arr[j - step];
j -= step;
}
arr[j] = temp;
}
System.out.println("step: " + step + " middle: " + Arrays.toString(arr));
step /= 2; //每次步长处理
}
}
基本排序算法(冒泡排序 选择排序 插入排序 快速排序 归并排序 基数排序 希尔排序)
原文:https://www.cnblogs.com/itchenguo/p/10677837.html