排序问题,总的分为两类:比较排序和非比较排序。
1、冒泡排序
对于要排序的数组,从第一位开始从前往后比较相邻两个数字,若前者大,则交换两数字位置,然后比较位向右移动一位。第1轮从前到后的比较将使得最大的数字 冒泡 到最后,此时可以说一个数字已经被排序。每一轮的比较将使得当前未排序数字中的最大者被排序,未排序数字总数减 1。第 arr.length - 1arr.length−1 轮结束后排序完成。
public class bubbleSort {
public static void main(String[] args) {
int arr[] = {1,5,8,3,2};
System.out.println("排序结果如下:");
bubbleSort bubbleman = new bubbleSort();
arr = bubbleman.bubble(arr);
for (int i = 0; i < arr.length; i++){
System.out.print(arr[i]+" ");
}
}
public int[] bubble(int[] arr){
if (arr.length < 2)
return arr;
// n - 1轮次执行,当前 n - 1 个元素排好后,最后一个元素无需执行,故i < arr.length - 1
for(int i = 0; i < arr.length - 1; i++){
boolean swapped = false;
for (int j = 0; j < arr.length - 1 - i; j++){
if (arr[j] > arr[j+1]){
swap(arr, j,j+1); //改变数组相邻位置的顺序
swapped = true;
}
}
//如果这一轮没有任何交换则说明:数组已经完全排序,退出大循环
if (swapped == false) break;
}
return arr;
}
//该方法不传递值,只是改变了数组的顺序
private void swap(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
2、选择排序
每一次从待排序的数组里找到最小值(最大值)的下标,然后将最小值(最大值)跟待排序数组的第一个进行交换,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。反复的进行这样的过程直到待排序的数组全部有序。 选择排序分为:单元选择排序(min或max)、双元选择排序(一轮遍历同时找出min和max)。
public class SelectSort {
public static void main(String[] args) {
int arr[] = {4,6,2,1,7,9,5,8,3};
System.out.println("排序结果如下:");
SelectSort sortway = new SelectSort();
arr = sortway.selectSort(arr);
for (int i = 0; i < arr.length; i++){
System.out.print(arr[i]+" ");
}
}
public int[] selectSort(int[] arr){
if (arr.length < 2)
return arr;
for (int i = 0; i < arr.length; i++){
int minindex = i;
for (int j = minindex; j < arr.length; j++){
if (arr[minindex] > arr[j]){
minindex = j;
}
}
// 然后进行交换,每一轮确定minindex指定的位置
swap(arr, i , minindex);
}
return arr;
}
//该方法不传递值,只是改变了数组的顺序
private void swap(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
3、插入排序
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。 插入排序也分为:简单插入排序、折半插入排序。
// 简单插入排序
public class InsertSort {
public static void main(String[] args) {
int arr[] = {4,6,2,1,7,9,5,8,3};
System.out.println("排序结果如下:");
InsertSort sortway = new InsertSort();
arr = sortway.insertSort(arr);
for (int i = 0; i < arr.length; i++){
System.out.print(arr[i]+" ");
}
}
public int[] insertSort(int[] arr){
if (arr.length < 2)
return arr;
for (int i = 1; i < arr.length; i++){
// 从数组的第二个值开始处理
int target = arr[i]; //保存目标值
int j;
for (j = i; j>0 && arr[j-1] > target; j--){ // 递减的一个迭代
// 把大于target的数往后移动,最后不大于target的数就空出来j
arr[j] = arr[j-1]; //该元素右移
}
arr[j] = target;
}
return arr;
}
}
4、希尔排序
希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。算法思想:希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
import java.util.Arrays;
public class ShellSort {
public static void main(String[] args) {
int[] arr={49,38,65,97,76,13,27,49,78,34};
shellSort(arr);
}
// static是静态修饰符,有全局的意思
public static void shellSort(int[] arr){
int count = 0; //表示第几轮
for (int gap = arr.length/2; gap>=1; gap = gap/2){ //步长
count++;
// 分组
for (int i = gap; i < arr.length; i++){
for (int j = i - gap; j >= 0; j = j - gap){
if (arr[j] > arr[j + gap]){
// 然后进行交换,每一轮确定minindex指定的位置
swap(arr, j , j + gap);
}
}
}
System.out.println("第"+count+"轮的排序结果");
System.out.println(Arrays.toString(arr)); //打印
}
}
//该方法不传递值,只是改变了数组的顺序
static void swap(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
5、归并排序
归并排序的核心思想是分治、递归。先将待排序的数组不断拆分,直到拆分到区间里只剩下一个元素的时候。不能再拆分的时候。这个时候我们再想办法合并两个有序的数组,得到长度更长的有序数组。当前合并好的有序数组为下一轮得到更长的有序数组做好了准备。一层一层的合并回去,直到整个数组有序。
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int [] array = {12, 56, 2, 8, 18, 35, 29};
System.out.println(Arrays.toString(array));
mergeSort(array, 0, array.length-1);
System.out.println(Arrays.toString(array));
}
public static int[] mergeSort(int[] array, int low, int high){
int mid = (low + high)/2; //记录中间值
if (low < high){
//分解:对数组进行递归(每一个数组分为两部分)
mergeSort(array, low, mid); //左边归并排序
mergeSort(array, mid+1, high); //右边归并排序
merge(array, low, mid, high); //合并(两个有序的数组)
}
return array;
}
public static void merge(int[] array, int low, int mid, int high){
int s1 = low; //第一个归并段的开始
int s2 = mid+1; //第二个归并段的开始
int[] ret = new int[high-low+1];
int i = 0; //表示数组下标
while(s1 <= mid && s2 <= high){ //越界检测
if (array[s1] <= array[s2]){ // 对比大小,调整顺序
ret[i++] = array[s1++];
}else {
ret[i++] = array[s2++];
}
}
while(s1<=mid){ //s1和s2如果还有剩余的
ret[i++] = array[s1++];
}
while(s2<=high){
ret[i++] = array[s2++];
}
//把ret数组的值,拷贝到array中
for (int j = 0; j < ret.length; j++){
array[j+low] = ret[j]; //不能乱放。从low开始
// System.out.println(ret[j]); // debug
}
// System.out.println("========");
}
}
6、快速排序
(1)通过从数组中选择一个中心元素将数组划分成两个子数组,在划分数组时,将比中心元素小的元素放在左子数组,将比中心元素大的元素放在右子数组。
(2)左子数组和右子数组也使用相同的方法进行划分,这个过程一直持续到每个子数组都包含一个元素为止。
(3)最后,将元素组合在一起以形成排序的数组。
通俗:先把数组中的一个数当作基准数,一般会把数组中最左边的数当作基准数。然后从两边进行检索。先从右边检索比基准数小的;再从左边检索比基准数大的。如果检索到了,就停下来,然后交换这两个元素(基准数和相遇位置的数交换),然后再继续检索。
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int [] array = {12, 56, 2, 8, 18, 35, 29};
System.out.println(Arrays.toString(array));
quickSort(array, 0, array.length-1);
System.out.println(Arrays.toString(array));
}
public static void quickSort(int[] arr, int low, int high){
if (arr.length < 2)
return;
if (low > high)
return;
int i = low;
int j = high;
// temp就是基准位
int temp = arr[low];
while (i<j){
//先看右边,依次往左边递减
while (arr[j]>=temp&&i<j){ //右边应该比temp要大
j--;
}
while (arr[i]<=temp&&i<j){ //左边应该比temp要小
i++;
}
if (i<j){ //符合条件就置换(最终是为了实现temp左边小,右边大)
int t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
//最后将基准temp与 i和j相等位置的数字进行交换
arr[low] = arr[i];
arr[i] = temp;
quickSort(arr, low, j-1); //递归调用左半数组
quickSort(arr, j+1, high); //递归调用右半数组
}
}
7、堆排序
首先,堆的结构可以分为大顶堆和小顶堆,是一个完全二叉树,而堆排序事根据堆的这种数据结构设计的一种排序。其中大顶堆的性质:每个结点的值都大于其左孩子和右孩子结点的值。
堆排序的基本思想:首先,将待排序的数组构造成一个大顶堆,此时,整个数组的最大值就是堆结构的顶端。然后,将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1。最后,将剩余的n-1个数再构造成大顶堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组。