文章目录
排序
概述:
排序是将一组“无序”的记录序列调整为“有序”的记录序列。分内部排序和外部排序。
内部排序:若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。【衡量内排序的效率是数据的比较次数】
外部排序:若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序。【衡量外排序的效率是内存与外存的交换次数】
内部排序的过程是一个逐步扩大记录的有序序列长度的过程。
不稳定排序跟稳定排序的区别就是稳定排序排序后能够确定相等的两个元素的位置排序后依旧是该位置顺序,例如Ai=Aj,排序后二者顺序依旧是Ai = Aj的顺序,不稳定排序排序后不能确定两个相等元素排序后二者的前后位置。
插入排序:
对数字找出对应的位置进行插入操作的排序算法。
概述:
每次处理就是将无序的数列中第一个元素与有序数列的元素从后到前比较,找到插入位置,将该元素插入到有序数列的适当的最终的位置上(稳定排序)。
复杂度:
时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 是否稳定 |
---|---|---|---|---|
O(n^2) | O(n^2) | O(n) | O(1) | 是 |
代码实现:
import java.util.Arrays;
public class insertsort {
public static void main(String[] args) {
int arr[] = {1,2,3,4,5,6,7,8,11,12,10};
insertsotr(arr);
System.out.println(Arrays.toString(arr));
}
public static void insertsotr(int []arr) {
int temp; //记录目前要交换数据的值
int index; //记录目前要排序的下标
boolean flag = false ; //判断是否进行交换
for(int i = 1;i < arr.length;i++) {
temp = arr[i];
index = i-1;
while(index>=0 && arr[index] > temp) { //循环查找是否 是该数字的位置
arr[index+1] = arr[index]; //不是则往前移动,是则将数字放入
index--;
if(flag == false) {
flag = true;
}
}
if(flag) { //若有发生数字移动则交换,没有则不换
if(index < 0) {
arr[0] = temp;
}else {
arr[index+1] = temp;
}
flag = false;
}
}
}
}
希尔排序:
概述:
希尔排序是插入排序的一种又称“缩小增量排序”,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
复杂度:
时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 是否稳定 |
---|---|---|---|---|
O(n Log n) | O(n Log^2 n) | O(n Log^2 n) | O(1) | 否 |
代码实现:
import java.util.Arrays;
public class ShellSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int arr[] = {1,2,3,4,5,6,7,8,9};
System.out.println(Arrays.toString(arr));
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void shellSort(int arr[]) {
int number = arr.length/2; //找出初始增量值
int i,j,k;
int temp;
while(number >= 1 ) { //当增量大于一时进行插入排序操作
for(j = number; j < arr.length ;j++) { //插入排序的变型,插入排序间隙为number
temp = arr[j];
i = j - number;
while( i >= 0 && arr[i] < temp ) {
arr[i + number] = arr[i];
i -= number;
}
arr[i + number] = temp;
}
number /= 2; //排序后将增量除以二
}
}
}
选择排序:
选择排序是一种简单直观的排序算法。
工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。
选择排序是不稳定的排序方法。
简单选择排序
复杂度:
时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 是否稳定 |
---|---|---|---|---|
O(n^2) | O(n^2) | O(n^2) | O(1) | 否 |
代码实现:
import java.util.Arrays;
public class SelectSort {
public static void main(String[] args) {
int []arr = {3,1,5,4,6,7,8,9,4};
selectsort(arr);
System.out.println(Arrays.toString(arr));
}
public static void selectsort(int []arr) {
int min; //最小值记录变量
int minindex,index; //最小值下标
int temp; //临时变量用于交换
for(int i = 0;i < arr.length;i++) { //将当前值设置为最小值并记录下下标
min = arr[i];
minindex = i;
index = i;
for(int j = i;j<arr.length;j++) { //找到无序区重最小值放到有序区最前面
if(min < arr[j]) {
min = arr[j];
minindex = j;
}
}
temp = arr[index];
arr[index] = min;
arr[minindex] = temp;
}
}
}
堆排序
基本思路:
-
将无序序列构建成一个堆,根据升序降序需求选择大顶堆(升序)或小顶堆(降序);
-
将堆顶元素与末尾元素交换,将最大/最小元素"沉"到数组末端;
-
重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。将数据分为有序区跟无序区,不断进行操作。
复杂度
时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 是否稳定 |
---|---|---|---|---|
O(N log2N) | O(N log2N) | O(Nl og2N) | O(1) | 否 |
代码实现:
public class HeapSort {
public static void main(String[] args) {
int[] arr = {4, 6, 8, 5, 9};
heapSort(arr);
System.out.println("排序后 = " + Arrays.toString(arr));
}
public static void heapSort(int[] arr) {
int temp = 0;
System.out.println("堆排序");
//将无序序列构建成一个堆,根据升序降序需求选择大项堆货小项堆
for(int i = arr.length / 2 - 1; i >= 0; i--) {
adjustHeap(arr, i, arr.length);
}
//将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端
//重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序
for(int j = arr.length - 1; j > 0; j--) {
//交换
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
adjustHeap(arr,0, j);
}
}
//将数组转换为最大堆
/**
* 功能:完成将以 i 为对应的非叶子结点的树调整成大项堆
* eg:int[] arr = {4, 6, 8, 5, 9} => i = 1 => adjustHeap => 得到 {4, 9, 8, 5, 6}
* 若再次调入 adjustHeap 则传入的是 i = 0 => 得到 {4, 9, 8, 5, 6} => {9, 6, 8, 5, 4}
* @param arr 待调整的数组
* @param i 表示非叶子结点在数组中索引
* @param length 表示对多少个元素继续调整,length是在逐渐的减少
*/
public static void adjustHeap(int[] arr, int i, int length) {
int temp = arr[i]; //先取出当前元素的值,保存在临时变量
//1.k = i * 2 + 1,k是 i 结点的左子结点
for(int k = i * 2 + 1; k < length; k = k * 2 + 1) {
if(k + 1 < length && arr[k] < arr[k + 1]) { //说明左子结点的值小于右子结点的值
k++; //k指向右子结点
}
if(arr[k] > temp) { //如果子结点大于父结点
arr[i] = arr[k]; //把较大的值赋给当前结点
i = k; //指向k,继续循环比较
}else {
break;
}
}
//当for 循环结束后,我们已经将以 i 为父结点的树的最大值,放在了最顶
arr[i] = temp; //将 temp 值放到调整后的位置
}
}
交换排序:
所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
冒泡排序
概述:
冒泡排序,是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
复杂度:
时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 是否稳定 |
---|---|---|---|---|
O(n^2)) | O(n^2) | O(n) | O(1) | 是 |
代码实现:
import java.util.Arrays;
public class BubleSort {
public static void main(String[] args) {
int arr[] = {1,2,3,4,5,6,7,8,9,10,11,23};
sort(arr);
System.out.println("a" + Arrays.toString(arr));
}
public static void sort(int[] arr) {
int temp;
for(int i = 0;i < arr.length;i++) {
for(int j = 0;j<arr.length-i;j++) {
if(j == arr.length-i-1 ) {
}
else if(arr[j] <= arr[j+1]) {
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
}
快速排序:
概述:
一种将目标分为以标兵值分为大于标兵与小于标兵值区域,并递归进行,直到目标的每一部分都有序的过程。
就是给基准数据找其正确索引位置的过程.
冒泡排序的一种改进。
复杂度:
时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 是否稳定 |
---|---|---|---|---|
O(n Log n) | O(n^2) | O(n Log n) | O(n Log n) | 否 |
代码实现:
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int arr[] = {1,3,5,7,9,2,4,8,6,10};
quickSort(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int arr[],int left,int right) {
int l = left; //记录排序数组的左右下标
int r = right;
int pirvot = arr[(left + right) /2]; //用数组中值做标兵
int temp = 0;
while (l < r) { //开始循环
while(arr[l] < pirvot) { //找到左边大于标兵值
l++;
}
while(arr[r] > pirvot) { //找到右边小于标兵值
r--;
}
if(l >= r ) { //判断是否完成排序
break;
}
temp = arr[l]; //交换找到的两个值
arr[l] = arr[r];
arr[r] = temp;
if(arr[l] == pirvot) { //如果当前换的的是标兵值,将下标往标兵换过去的方向走一步,避免标兵的值的丢失,并且若换过来的是标兵值,说明一边已全符合要求
r--;
}
if(arr[r] == pirvot) {
l++;
}
if(left < r) {
quickSort(arr, left, r);
}
if(right > l) {
quickSort(arr, l, right);
}
}
}}
归并排序:
概述:
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。归并排序是一种稳定的排序方法。
先分后合。
复杂度:
时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 是否稳定 |
---|---|---|---|---|
O(n Log n) | O(n Log n) | O(n Log n) | O(n) | 是 |
代码实现:
import java.util.Arrays;
public class MergetSort {
public static void main(String[] args) {
int arr[] = {1,2,3,6,8,0,9,4,5,8,9,7,4};
int target[] = new int[arr.length];
merge(arr, 0, arr.length - 1, target);
System.out.println(Arrays.toString(target));
}
//合并的函数
public static void mergetSort(int arr[],int left,int mid,int right,int target[]) {
int l = left;
int midIndex = mid + 1;
int tagIndex = 0;
while(l <= mid && midIndex <= right) {
if(arr[l] <= arr[midIndex]) {
target[tagIndex++] = arr[l++];
}else {
target[tagIndex++] = arr[midIndex++];
}
}
while(l <= mid) {
target[tagIndex] = arr[l];
tagIndex++;
l++;
}
while(midIndex <= right){
target[tagIndex] = arr[midIndex];
midIndex++;
tagIndex++;
}
//装
int targetLeft = left;
int t = 0;
while (targetLeft <= right) {
arr[targetLeft++] = target[t++];
}
}
//拆分的函数
public static void merge(int arr[],int left,int right,int []temp) {
if(left < right) {
int mid = (left + right) / 2;
//向左分
merge(arr,left,mid,temp);
//向右分
merge(arr,mid+1,right,temp);
//分完合并
mergetSort(arr,left,mid,right,temp);
}
}
}
基数排序:
概述:
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort。
顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O(nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。
复杂度:
n: 数据规模
k:桶的个数
时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 是否稳定 |
---|---|---|---|---|
O(n * k) | O(n * k) | O(n * k) | O(n + k) | 是 |
代码实现:
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
int [] arr = {9,8,7,10,123,456,789,654,321,987};
radixSort(arr);
System.out.println(Arrays.toString(arr));
}
//基数排序方法
public static void radixSort(int arr[]) {
//定义一个二维数组表示桶
//基数排序属于空间换时间的经典算法
int [][] bucket = new int [10][arr.length];
//用一个一维数组来表示每个桶存入的个数
int [] bucketElmentsCounts = new int [10];
int max = arr[0];
for(int i = 1;i < arr.length;i++) {
if(arr[i] > max) {
max = arr[i];
}
}
int maxLength = (max + "").length();
for(int i = 0,n = 1;i < maxLength;i++,n *= 10) {
//获取元素下标
for(int j = 0;j < arr.length;j++) {
//获取下标
int digitOfElement = arr[j] / n % 10;
//放桶
bucket[digitOfElement][bucketElmentsCounts[digitOfElement]++] = arr[j];
}
int index = 0;
for(int k = 0;k < bucketElmentsCounts.length;k++) {
if(bucketElmentsCounts[k]!=0) {
for(int l = 0;l < bucketElmentsCounts[k];l++) {
arr[index++] = bucket[k][l];
}
}
//把桶下表清零
bucketElmentsCounts[k] = 0;
}
}
}
}