数组排序问题
1.冒泡排序算法
冒泡排序(Bubble Sort) - 通过不断交换相邻元素的位置,逐步将最大或最小的元素"冒泡"到序列的两端。
思路:
冒泡排序算法的一趟原理如下:
相邻元素进行两两比较,如果前者大于后者,则交换,否则不变。
每一趟依次向后进行第1步,直到最后一个元素。
经过一趟的依次比较后的结果是,最后一个数是本趟比较中最大的数。所谓大数 "沉入水底" ,轻的气泡 "向上冒泡"。
继续重复1、2、3 步骤。每趟只比较到上一步剩下的部分即可(除去最后的已经 "沉到底部" 的元素)。
详细步骤如下:
冒泡排序 :比较次数(内层循环) 和 趟数(外层循环)
[7 3 2 1 9]:
每一趟 找出最大值放到最后
- 总结规律:i<length-1 j<length-i-1
- 第一趟:i=0 j<=3
- 比较第一次:(j和j+1)
- j=0:3 7 2 1 9
- j=1:3 2 7 1 9
- j=2:3 2 1 7 9
- j=3:3 2 1 7 [9]
- 第二趟:i=1 j<=2
- j=0:2 3 1 7 9
- j=1:2 1 3 7 9
- j=2:2 1 3 [7 9]
- 第三趟:i=2 j<=1
- j=0:1 2 3 7 9
- j=1:1 2 [3 7 9]
- 第四趟:i=3 j<=0
- j=0:1 [2 3 7 9]
- 总结规律:i<length-1 j<length-i-1
- 交换(j和j+1)
具体代码如下:
java
public static void maopao(){
int[] arr = {7,3,12,1,0};
for (int i = 0; i < arr.length-1; i++) {
for (int j = 0; j < arr.length-i-1; j++) {
if(arr[j]>arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
2.选择排序算法:
选择排序(Selection Sort) - 每一轮在剩余未排序的元素中找到最小(或最大)的元素,放到已排序部分的末尾
思路:
选择排序每趟选择一个固定位置上的元素,向后逐一和此位置上的数进行比较,如果小于此位置上的数,则交换。
一趟结束后,此位置上的数就是当前排序序列中最小的元素了。
重复上面的步骤即可排序成功。
步骤举例:
- 选择排序:每次拿i上的元素和后面的数一一比较
- [3,6,9,2,7] i 和 j 比较+
- 第一趟:i=0:[3] 6 9 2 7 | 1<=j<=4
- j=1:[3] 6 9 2 7 --arr[i]=3和arr[j]=6比较
- j=2:[3] 6 9 2 7 --arr[i]=3和arr[j]=9比较
- j=3:[2] 6 9 3 7 --arr[i]=3和arr[j]=2比较
- j=4:[2] 6 9 3 7 --arr[i]=2和arr[j]=7比较
- 第二趟:i=1: 2[6]9 3 | 2<=j<=4
- j=2:2[6]9 3 7 ---arr[i]=6和arr[j]=9比较
- j=3:2[3]9 6 7 ---arr[i]=6和arr[j]=3比较---
- j=4:2[3]9 6 7 ---arr[i]=3和arr[j]=7比较---
- 第3趟:i=2: 2 3[9] 6 7 | 3<=j<=4
- j=3:2 3 [6] 9 7 ---arr[i]=9和arr[j]=6比较---
- j=4:2 3 [6] 9 7 ---arr[i]=6和arr[j]=7比较---
- 第4趟:i=3: 2 3 6[9]7 | 4<=j<=4
- j=4: 2 3 6[7]9 ---arr[i]=9和arr[j]=7比较---
java
/**
* 总结:
* i--0~length-1
* j--i+1~length-1
* 比较:
* i和j
*/
public static void selorder(int[] arr){
// int[] arr = {3,6,9,2,7};
for (int i = 0; i < arr.length-1; i++) {
for (int j = i+1; j <= arr.length-1; j++) {
if(arr[i]>arr[j]){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
//System.out.println(Arrays.toString(arr));
}
3.插入排序算法:
插入排序(Insertion Sort) - 将每个元素按其正确位置插入已排序的部分,保持已排序部分始终有序
思路:
插入排序思路:
从第二个位置上开始,每次选择一个位置上元素,和前面的数逐一比较,如果小于前面的数,则前面数后移,直到大于前面的数,则停止移动,插入位置。这样每趟走完之后,前面的已经插入的序列一定是有序。
就如生活中的起扑克牌一样,每起一张都会插入手中,但是插入后,手中的牌一直是有序的。
步骤举例
- 插入排序
- 第一趟:假设第一个元素是有序的,从第二个开始和前面有序的序列中插入到合适位置
- 第一趟:[9] (2) 1 7 4 3 6
- 第一次:[2 9] 1 7 4 3 6
- 第二趟:[2 9] (1) 7 4 3 6
- 第一次:2 1 9 7 4 3 6
- 第二次:1 2 9 7 4 3 6
- 第三趟:[1 2 9] (7) 4 3 6
- 第一次:...
- 插入排序每一趟的结果是保证前面的是有序的序列。
java
public static void insertOrder() {
int[] arr = {9,8,7,6,5,4,3,2};
for (int i = 1; i < arr.length; i++) {
int insert = arr[i];
for (int j = i; j > 0; j--) {
//前面的数大于已经选择插入的数,则后移
if(arr[j-1]>insert) {
arr[j] = arr[j-1];
arr[j-1] = insert;
}else {
arr[j] = insert;
break;
}
}
}
System.out.println("排序后:"+Arrays.toString(arr));
}
4.快速排序算法
快速排序(Quick Sort) - 使用分治策略,选取一个基准元素,将数组划分为两部分,左边的元素都小于基准,右边的元素都大于基准,然后递归地对左右两边进行快速排序
思路:
需要使用到递归
每次选择一个元素,通过一趟的过程,前后比较,从而找到自己的合适位置,即此位置前的都比次数小,此位置之后的,都比次数大。
然后再对已经分割的两部分,重复递归使用相同的方法来找每个数的位置。递归嵌套即可完成排序。
步骤举例
- 快速排序
- 初始数组:4 6 9 7 8 1 3
- 开始选择第一数,分别和对称其他位置上的数比较,找到合适位置
- 第一趟:和最后一位的3比较后结果为: 【4】 6 9 7 8 1 3
- 第一次: 3 6 9 7 8 1 [4 ]
- 第二次: 3 [4 ] 9 7 8 1 6
- 第三次: 3 1 9 7 8 [4 ] 6
- 第四次:3 1 [4 ] 7 8 9 6
- 第五次:3 1 [4 ] 7 8 9 6 和8比较 不要交换,所以和第四次一致
- 第六次:3 1 [4 ] 7 8 9 6 同上
- 注意:第一趟完成后,我们可以看到,4 所在位置之前的都比4小,之后的都比4大
- 下面再分别对于前后两部分使用相同的思路进行交换即可
- 第二趟:3 1 [4 ] 7 8 9 6
- 第一次:{1 3 } 4 {6 8 9 7} 说明:这里同时找前面一组中1的位置,和后面一组中7的位置
- 第二次:{1 3 } 4 {6 7 9 8}
- 第三次:{1 3 } 4 {6 7 9 8}
- 第二趟结束
- 第三趟:1 3 4 6 7{ 9 8}
- 第一次:1 3 4 6 7{ 8 9}
java
package com;
import java.util.Arrays;
public class QuickSort {
public static void quickSort(int[] arr,int low,int high){
int i,j,temp,t;
if(low>high){
return;
}
i=low;
j=high;
//temp就是基准位
temp = arr[low];
while (i<j) {
//先看右边,依次往左递减
while (temp<=arr[j]&&i<j) {
j--;
}
//再看左边,依次往右递增
while (temp>=arr[i]&&i<j) {
i++;
}
//如果满足条件则交换
if (i<j) {
t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
//最后将基准为与i和j相等位置的数字交换
arr[low] = arr[i];
arr[i] = temp;
//递归调用左半数组
quickSort(arr, low, j-1);
//递归调用右半数组
quickSort(arr, j+1, high);
}
public static void main(String[] args){
int[] arr = {10,7,2,4,7,62,3,4,2,1,8,9,19};
quickSort(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
}
5. 希尔排序
希尔排序(Shell Sort) - 是插入排序的一种优化版本,通过定义一个间隔序列来分组元素,使得不同组内的元素相对较远,从而减少整体的交换次数。
以下是一个简单的 Java 语言实现希尔排序的示例。在这个例子中,我将展示一种基于经典希尔排序算法思想的实现,其中采用的是Hibbard增量序列(即增量每次都为前一次的一半,直到增量为1)。
java
public class ShellSort {
/**
* 希尔排序(Hibbard增量序列)
*
* @param arr 待排序的整数数组
*/
public static void shellSort(int[] arr) {
// 获取数组长度
int n = arr.length;
// 开始时设定一个较大的步长(通常是n/2,这里简化为直接使用常量)
int gap = n / 2;
while (gap > 0) {
// 按照当前步长进行插入排序
for (int i = gap; i < n; i++) {
// 将arr[i]插入到arr[i-gap], arr[i-2*gap]...已经排序好的序列中
int temp = arr[i];
int j;
for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
arr[j] = arr[j - gap];
}
// 插入元素
arr[j] = temp;
}
// 缩小增量
gap /= 2; // 或者 gap = (gap == 2) ? 1 : gap / 2; 这种方式可以防止gap变为0
}
}
public static void main(String[] args) {
int[] arr = {5, 1, 7, 3, 1, 6, 9, 4};
shellSort(arr);
// 输出排序后的数组
for (int num : arr) {
System.out.print(num + " ");
}
}
}
这段代码首先设置了一个初始步长(gap
),然后在每一次循环中,都会对所有按照步长分组的子序列进行插入排序。随着步长逐渐减小至1,整个数组就被分成了越来越多的小组,最终实现全局有序。当步长减小到1时,实际上就是执行了一次完整的插入排序,此时数组已经是有序的了。