排序
1、冒泡排序
直接贴代码:自己写的
//冒泡排序
/*1.比较数组中,两个相邻的元素,如果第一个数比第二个数
大,我们就交换他们的位置 */
//2.每一次比较,都会产生出一个最大,或者最小的数字;
//3.下一轮则可以少一次排序!
//4.依次循环,直到结束
public static void main(String[] args) {
int[] a = {1,2,3,5,4};
a = sort(a);
System.out.println(Arrays.toString(a));
}
public static int[] sort(int[] b){
int temp = 0; //作为第三个变量 用来交换用的
int x = 0,y = 0; //x用来记录一共执行了多少次交换
for (int j = b.length - 1; j > 0; j--) {
for (int i = 0; i < j; i++) { //最大的数被换到最后,下一次排序可以不对它进行判断
if(b[i] > b[i + 1]){
temp = b[i];
b[i] = b[i + 1];
b[i + 1] = temp;
x++;
}
}
if(x == y){ //如果此次循环没有进行交换 说明数组已经完全排序好了 不用再进行多余的排序
break;
}else {
y = x;
}
}
System.out.println(x);
return b;
}
优化算法:
public static int[] sort(int[] b){
int temp = 0;
boolean flag = true; //取消计数功能 用一个布尔值进行判断
for (int j = b.length - 1; j > 0; j--) {
for (int i = 0; i < j; i++) {
if(b[i] > b[i + 1]){
temp = b[i];
b[i] = b[i + 1];
b[i + 1] = temp;
flag = false;//数组做了更改 布尔值变为false
}
}
if(flag){
break; //当数组一次循环没有任何修改 说明该数组已经排好序,跳出循环
}
flag = true;//做了更改,把布尔值变回true,以便下一次循环判断。
}
return b;
}
思考:如果传进来的数组后面已经是排好序的,后面的循环再去排序就是浪费时间(前面这种算法的所有可能性包含在这种算法中)
public static int[] sort(int[] b){
int temp = 0;
int flag = 0;//初始值为0
for (int j = b.length - 1; j > 0; j--) {
for (int i = 0; i < j; i++) {
if(b[i] > b[i + 1]){
temp = b[i];
b[i] = b[i + 1];
b[i + 1] = temp;
flag = 0;//做出更改 值修正为0
}else{
flag++;//没做出改变 值加1
}
}
j -= flag;//最后有几个数没有做出改变 用外层循环的j减去这个数字
}
return b;
}
思考:如果前面已经是排好的,那每次循环开始也要进行没必要的判断,使用双向排序
public static int[] sort(int[] b){
int temp = 0;
int flag = 0,//记录数组有没有做出改变
flagA = 0,flagB = 0;//把改变的值储存在A B里 A为正向排序的 B为反向排序的
for (int j = b.length - 1,k = 0; j > 0 && flagA + flagB < b.length - k; j--,k++) {
//k为计数 记录循环执行了多少次
//正向
for (int i = flagB + k; i < j - flagA; i++) {
//循环从flagB + k开始 从j - flagA结束
//flagB + k之前的和j - flagA之后的都是已经排序好的 排序算法没必要对其进行多余的排序
if(b[i ] > b[i + 1]){
temp = b[i];
b[i] = b[i + 1];
b[i + 1] = temp;
flag = 0;//做改变 flag值修正
}else{
flag++;//没做改变 flag值加一
}
}
flagA += flag;//把改变的值储存在
flag = 0;//修正
//反向
for (int i = j - 1 - flagA; i > flagB + k; i--) {
//循环从j - flagA -1开始
//(经过上面的排序最大的数字已经在最后了 没有必要再对它进行排序)
//后面同理
if(b[i] < b[i - 1]){
temp = b[i];
b[i] = b[i - 1];
b[i - 1] = temp;
flag = 0;
}else{
flag++;
}
}
flagB += flag;
flag = 0;
}
return b;
能不能看懂全看天意。
优化一下:
public static int[] sort(int[] b) {
int temp = 0;
int flag = 0;//记录数组有没有做出改变
for (int flagA = 0, flagB = 0; flagA + flagB < b.length; ) {
//把改变的值储存在A B里 A为正向排序的 B为反向排序的
for (int j = flagA; j < b.length - 1 - flagB ; j++) {
//循环从flagA开始 从b.length - 1 - flagB结束
//b.length - 1 - flagB之前的和flagA之后的都是已经排序好的 排序算法没必要对其进行多余的排序
if (b[j] > b[j + 1]) {
temp = b[j + 1];
b[j + 1] = b[j];
b[j] = temp;
flag = 0;//做改变 flag值修正
}else {
flag++;//没做改变 flag值加一
}
}
flagB += flag + 1;//把改变的值储存在flagA中 + 1 是因为上面已经排序好了一位
flag = 0;
for (int j = b.length - 1 - flagB ; j > flagA ; j--) {
if (b[j] < b[j - 1]) {
temp = b[j - 1];
b[j - 1] = b[j];
b[j] = temp;
flag = 0;
}else {
flag ++;
}
}
flagA += flag + 1;
flag = 0;
}
return b;
}
2、选择排序
/**
选择排序
1.从0所有处开始,依次和后面的元素比较,小元素往前放,
那么经过一轮比较之后,最小的元素,就会出现在最小索引处
2.第二轮从第二个元素开始比较。
3.比较完成后,数组就会变成排序好的样子
*/
public class Demo04 {
public static void main(String[] args) {
int[] array = {34,55,78,98,22};
for (int i = 0; i < array.length; i++) {
for (int j = i; j < array.length; j++) {
int temp = 0;
if (array[i] > array[j]){
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
System.out.println(Arrays.toString(array));
}
}
3、插入排序(直接插入排序)
/**
* 插入排序
* 将一个记录插入到一个长度为m的有序表中,使之保持有序
* 例如:49,38,65,97,76,13,27 原始数据
* <p>
* 【49】,38,65,97,76,13,27 从1索引开始插入
* 【38,49】,65,97,76,13,27
* 【38,49,65】,97,76,13,27
* 【38,49,65,97】,76,13,27
* 【38,49,65,76,97】,13,27
* 【13,38,49,65,76,97】,27
* 【13,27,28,49,65,76,97】
*/
public class ArrayDemo14 {
public static void main(String[] args) {
int[] a = {49, 38, 65, 97, 76, 13, 27};
for (int i = 1; i < a.length; i++) {
for (int j = i; j > 0 ; j--) {
if (a[j] < a[j - 1]){
int temp = a[j];
a[j] = a[j - 1];
a[j - 1] = temp;
}
}
}
System.out.println(Arrays.toString(a));
}
}
4、希尔排序
/**
* 希尔排序
* 1.先将原表按增量ht分组
* 2每个子文件按照直接插入法排序。
* 3.同样,用下一个增量ht/2将文件再分为子文件,再直接插入排序法。
* 4.直到ht=1时整个文件排好序。
* <p>
* 关键:选择合适的增量。
*/
public class ArrayDemo16 {
public static void main(String[] args) {
int[] a = {213, 145, 22, 40, 8, 49, 203, 198, 41, 188, 66, 173, 150,
251, 129};
int gap = a.length;
while (true) {
gap = gap / 2;
for (int i = 0; i < gap; i++) {
for (int j = i; j < a.length; j += gap)
for (int k = j; k > i ; k -= gap) {
if (a[k] < a[k - gap]) {
swap(a, k, k - gap);
}
}
}
if (gap == 1) {
break;
}
}
System.out.println(Arrays.toString(a));
}
}
public static void swap(int[] array, int i, int j){
int temp = 0;
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
5、快速排序
/**
* 快速排序
* 1.从数组中取出一个数,作为基准数。
* 2.分区:将比这个数大或者等于的数全放在他的右边,小于他的数全放到他的左边。
* 3.再对左右区间重复第二步,直到各区间只有一个数。
* <p>
* 实现思路:
* 挖坑填数
* 1.将基准数挖出形成第一个坑。
* 2.由后面找出比他小的数,找到后挖出此数填到前一个坑中。
* 3.由前向后找比他大或者等于的数,找到后也挖出此数填到前一个坑中,
* 4.重复2,3步骤
*/
public class ArrayDemo17 {
public static void main(String[] args) {
int[] a = {5, 9, 8, 7, 6, 10, 4, 3, 2, 1};
int start = 0;
int end = a.length - 1;
quickSort(a, start, end);
System.out.println(Arrays.toString(a));
}
private static void quickSort(int[] a, int start, int end) {
if (start < end) {
int index = getIndex(a, start, end);
quickSort(a, start, index);
quickSort(a, index + 1, end);
}
}
private static int getIndex(int[] a, int start, int end) {
int i = start;
int j = end;
int x = a[i];
while (i < j) {
while (i < j && a[j] > x) {
j--;
}
if (i < j) {
a[i] = a[j];
i++;
}
while (i < j && a[i] <= x) {
i++;
}
if (i < j) {
a[j] = a[i];
j--;
}
}
a[i] = x;
return i;
}
}
6、归并排序
/**
* 归并排序 就是利用归并的思想实现排序的方法。 又称为二路归并排序
* <p>
* 原理:
* 1.假设初始序列有N个记录,则可以看成是N个有序的子序列
* 2.每个子序列的长度为1,然后两两归并,
* 3.得到N/2个长度为2或1的有序子序列,再两两归并
* 4.重复步复,直到得到一个长度为N的有序序列为止,
*/
public class ArrayDemo18 {
public static void main(String[] args) {
int[] a = {8, 5, 61, 18, 1, 31, 554, 6, 31, 1335, 31, 1321, 30, 3213, 3};
//拆分
chaiFen(a, 0, a.length);
//归并
//guiBing(a,0,a.length / 2 ,a.length );
System.out.println(Arrays.toString(a));
}
private static void chaiFen(int[] a, int startIndex, int endIndex) {
int centerIndex = (endIndex + startIndex) / 2;
if (endIndex > startIndex + 1) {//加一是为了避免(5 + 4)/ 2 = 4 然后一直递归下去
chaiFen(a, startIndex, centerIndex);
chaiFen(a, centerIndex, endIndex);
guiBing(a, startIndex, centerIndex, endIndex);
}
}
private static void guiBing(int[] a, int startIndex, int centerIndex, int endIndex) {
int[] tempArr = new int[endIndex - startIndex];//临时数组的长度为尾 - 头
int index = 0;
int i = startIndex;
int j = centerIndex;
while (i < centerIndex && j < endIndex) {
if (a[i] < a[j]) {
tempArr[index] = a[i];
i++;
} else {
tempArr[index] = a[j];
j++;
}
index++;
}
while (i < centerIndex) {
tempArr[index] = a[i];
index++;
i++;
}
while (j < endIndex) {
tempArr[index] = a[j];
j++;
index++;
}
for (int k = 0; k < tempArr.length; k++) {
a[k + startIndex] = tempArr[k];
}
}
}
7、基数排序
第一步
以LSD为例,假设原来有一串数值如下所示:
73, 22, 93, 43, 55, 14, 28, 65, 39, 81
首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:
0
1 81
2 22
3 73 93 43
4 14
5 55 65
6
7
8 28
9 39
第二步
接下来将这些桶子中的数值重新串接起来,成为以下的数列:
81, 22, 73, 93, 43, 14, 55, 65, 28, 39
接着再进行一次分配,这次是根据十位数来分配:
0
1 14
2 22 28
3 39
4 43
5 55
6 65
7 73
8 81
9 93
第三步
接下来将这些桶子中的数值重新串接起来,成为以下的数列:
14, 22, 28, 39, 43, 55, 65, 73, 81, 93
这时候整个数列已经排序完毕;如果排序的对象有三位数以上,则持续进行以上的动作直至最高位数为止。
LSD的基数排序适用于位数小的数列,如果位数多的话,使用MSD的效率会比较好。MSD的方式与LSD相反,是由高位数为基底开始进行分配,但在分配之后并不马上合并回一个数组中,而是在每个“桶子”中建立“子桶”,将每个桶子中的数值按照下一数位的值分配到“子桶”中。在进行完最低位数的分配后再合并回单一的数组中
/**
* 基数排序
* 基数排序不同与其他的各类排序
* 前边介绍到的排序方法或多或少的是通过使用比较个移动记录来实现排序,
* 而基数排序的实现不需要进行对关键字的比较,
* 只需要对关键字进行“分配”与“收集”两种操作即可完成。
*/
public class ArrayDemo19 {
public static void main(String[] args) {
int[] a = {8, 5, 61, 18, 1, 31, 554, 6, 31, 1335, 31, 1321, 30, 3213, 3};
sortArr(a);
// int max = getMax(a);
System.out.println(Arrays.toString(a));
}
private static void sortArr(int[] a) {
//建立10个桶
int[][] tempArr = new int[10][a.length];
//获取最大元素
int max = getMax(a);
//获取最大元素的位数
int len = String.valueOf(max).length();
//循环轮次
for (int i = 0, temp = 1; i < len; i++, temp *= 10) {
//建立统计数组 并在进入循环中是清空统计数组中的元素
int[] counts = new int[10];
for (int j = 0, flag = 0; j < a.length; j++) {
//获取每位上的数子
flag = a[j] / temp % 10;
tempArr[flag][counts[flag]++] = a[j];
}
//取出桶中的元素
for (int j = 0, index = 0; j < counts.length; j++) {
for (int k = 0; k < counts[j]; k++) {
//从桶中取出元素放回原数组
a[index++] = tempArr[j][k];
//清空桶中的元素
tempArr[j][k] = 0;
}
}
}
}
private static int getMax(int[] a) {
int max = a[0];
for (int i = 1; i < a.length; i++) {
if (a[i] > max) {
max = a[i];
}
}
return max;
}
}
8、堆排序
/**
* 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序
* <p>
* 堆排序的基本思想是:
* 1.讲待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点
* 2.将其与末尾元素进行交换,此时末尾就为最大值
* 3.然后将剩余的n - 1个元素重新构造成一个堆,这样会得到n个次小值。
* 4.如此反复执行,便能得到一个有序序列了。
*/
public class ArrayDemo20 {
public static void main(String[] args) {
int[] a = {81, 175, 73, 113, 239, 75, 17, 106, 119, 241,163, 59, 51, 115, 112};
a= heapSort(a);
System.out.println(Arrays.toString(a));
}
/**
* 选择排序-堆排序
* @param array 待排序数组
* @return 已排序数组
*/
public static int[] heapSort(int[] array) {
//这里元素的索引是从0开始的,所以最后一个非叶子结点array.length/2 - 1
for (int i = array.length / 2 - 1; i >= 0; i--) {
adjustHeap(array, i, array.length); //调整堆
}
// 上述逻辑,建堆结束
// 下面,开始排序逻辑
for (int j = array.length - 1; j > 0; j--) {
// 元素交换,作用是去掉大顶堆
// 把大顶堆的根元素,放到数组的最后;换句话说,就是每一次的堆调整之后,都会有一个元素到达自己的最终位置
swap(array, 0, j);
// 元素交换之后,毫无疑问,最后一个元素无需再考虑排序问题了。
// 接下来我们需要排序的,就是已经去掉了部分元素的堆了,这也是为什么此方法放在循环里的原因
// 而这里,实质上是自上而下,自左向右进行调整的
adjustHeap(array, 0, j);
}
return array;
}
/**
* 整个堆排序最关键的地方
* @param array 待组堆
* @param i 起始结点
* @param length 堆的长度
*/
public static void adjustHeap(int[] array, int i, int length) {
// 先把当前元素取出来,因为当前元素可能要一直移动
int temp = array[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 && array[k] < array[k + 1]) { //如果有右子树,并且右子树大于左子树
k++;
}
//如果发现结点(左右子结点)大于根结点,则进行值的交换
if (array[k] > temp) {
swap(array, i, k);
// 如果子节点更换了,那么,以子节点为根的子树会受到影响,所以,循环对子节点所在的树继续进行判断
i = k;
} else { //不用交换,直接终止循环
break;
}
}
}
/**
* 交换元素
* @param arr
* @param a 元素的下标
* @param b 元素的下标
*/
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}