排序算法的基本分類
1.插入排序
插入排序 時間複雜度是O(n^2) 不穩定
插入排序介紹:對於欲排序的元素以插入的方式尋找該元素的適當位置,以達到排序的目的 * 插入排序思想:把n個待排序的元素看成未一個有序表和一個無序表,開始時有序表中只包含一個元素, * 無序表中取出第一個元素,把它的排序碼一次與有序表中的排序碼進行比較, * 將它插入到有序表中的適當位置,使之成爲新的有序表 * 插入排序的缺點:當需要插入的數是較小的數時,數據向後移動的次數明顯增多
public static void insertSort(int[] array) {
for (int i = 1; i < array.length; i++) {
int insertValue = array[i];
int insertIndex = i - 1;
while (insertIndex >= 0 && insertValue < array[insertIndex]) {
//將當前被比較過的有序表中的元素向後移動一位
array[insertIndex + 1] = array[insertIndex];
//繼續往前找
insertIndex--;
}
//如果待插入的數insertValue小於有序列表中的所有數,
// 或者待插入的數大於當前數組下標爲insertIndex的數
//當退出循環時說明待插入的位置找到了,爲insertIndex+1
if (insertIndex + 1 != i) {
array[insertIndex + 1] = insertValue;
}
//System.out.println("第"+(i+1)+"輪後的順序是:"+ Arrays.toString(array));
}
System.out.println("插入排序後的順序是:" + Arrays.toString(array));
}
2.希爾排序
希爾排序 時間複雜度是O(n log n) 不穩定
希爾排序是簡單插入排序的更高效版本,也稱爲縮小增量排序 * 希爾排序的思想:就是把記錄數按下標的一定增量分組,對每組使用世界插入排序算法排序, * 隨着增量逐漸減少,每組包含的關鍵詞越來越多,當增量減少至一時, * 整個文件恰被分成一組,算法便終止 * 希爾排序時,對有序序列在插入時採用交換法,ShellSortExchange() * 希爾排序時,對有序序列在插入時採用交換法,ShellSortMove()
public static void shellSortExchange(int[] array) {
int temp = 0;//用於臨時存放交換的元素
int count = 0;//用於記錄當前是第幾輪排序
//gap即數組被分爲gap數個小組,
for (int gap = array.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < array.length; i++) {
//按照插入排序的方法 遍歷各個組中的所有元素,共有gap組
for (int j = i - gap; j >= 0; j -= gap) {
//如果當前元素大於加上步長後的那個元素,說明交換
if (array[j] > array[j + gap]) {
temp = array[j];
array[j] = array[j + gap];
array[j + gap] = temp;
}
}
}
// System.out.println("希爾排序第"+(++count)+"輪排序後的順序是:"+Arrays.toString(array));
}
System.out.println("希爾交換式排序後的順序是:" + Arrays.toString(array));
}
public static void shellSortMove(int[] array) {
//增量gap,並逐步縮小增量
for (int gap = array.length / 2; gap > 0; gap /= 2) {
//從第gap個元素,逐個對其所在的組進行直接插入排序
for (int i = gap; i < array.length; i++) {
int insertIndex = i;
int insertValue = array[insertIndex];
if (array[insertIndex] < array[insertIndex - gap]) {
while (insertIndex - gap >= 0 && insertValue < array[insertIndex - gap]) {
//移動
array[insertIndex] = array[insertIndex - gap];
insertIndex -= gap;
}
//當退出while後,就給insertValue找到了插入的位置
array[insertIndex] = insertValue;
}
}
}
System.out.println("希爾移位式排序後的順序是:" + Arrays.toString(array));
}
3.選擇排序
選擇排序 時間複雜度是O(n^2) 不穩定
* 選擇排序介紹:選擇式排序是從欲排序的數據中,按指定的規則選出某一元素,再依規定交換位置後達到選擇排序的目的 * 選擇排序思想:第一次從arr[0]~ar[n-1]中選取最小值與arr[0]交換, * 第i次從arr[i-1]~arr[n-1]中選最小值與arr[i-1]交換, * 第n-1次從arr[i-2]~arr[n-1]中選最小值與arr[n-2]交換, * 得到一個按從小到大排序的有序序列
public static void selectSort(int[] array) {
for (int i = 0; i < array.length; i++) {
int minIndex = i;//每次存放找到的最小值的下標
int min = array[i];//存放最小值
for (int j = i + 1; j < array.length; j++) {
if (min > array[j]) {
min = array[j];//重置min
minIndex = j;//重置minIndex
}
}
//將最小值與當前遍歷到的值交換
if (minIndex != i) {
array[minIndex] = array[i];
array[i] = min;
}
//System.out.println("第"+(i++)+"輪後");
}
System.out.println("選擇排序排序後的順序是:" + Arrays.toString(array));
}
4.冒泡排序
冒泡排序 時間複雜度是O(n^2) 穩定
冒泡排序思想:通過對待排序序列從前向後,依次比較相鄰元素的值, * 如發現逆序則交換,使值較大的元素從前移動到後面, * 就像水底下的氣泡一樣組件向上冒 * 冒泡排序優化:如果一趟比較下來沒有進行過交換,說明序列有序, * 因此再排序過程中設置一個標誌flag,判單是否進行過交換, * 從而減少不必要的比較
public static void bubbleSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) {//這裏注意 -1因爲每趟排序都是把最大的數交換到了當前排序數的最後面
int temp = 0;
boolean flag = true;
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] > array[j + 1]) {
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
flag = false;
}
}
//System.out.println("第"+(i+1)+"趟排序後端數組:"+Arrays.toString(array));
if (flag = true) {
//代表沒有發生交換,數組有序,就不用交換了
break;
}
}
System.out.println("冒泡排序排序後的順序是:" + Arrays.toString(array));
}
5.快速排序
快速排序 時間複雜度是O(n log n) 不穩定
* 快速排序是對冒泡排序的一種改進,也屬於交換排序 * 快速排序思想:通過一趟排序將要排序的數據分割成獨立的兩部分, * 其中一部分的所有數據都比另外一部分的所有數據都要小, * 然後再按照此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行, * 以此達到整個數據編程有序序列
public static void quickSort(int[] array, int left, int right) {
int l = left;//左下標
int r = right;//右下標
int pivot = array[(left + right) / 2];//中軸值
int temp = 0;//臨時變量
//while循環的目的是讓比pivot值小的都在pivot的左邊,比pivot值大的都在pivot的右邊
while (l < r) {
//從左向右找大於pivot值
while (array[l] < pivot) {
l += 1;
}
//從右向左找小於pivot的值
while (array[r] > pivot) {
r -= 1;
}
//左右兩邊的值符合規則
if (l >= r) {
break;
}
//交換找到的值
temp = array[l];
array[l] = array[r];
array[r] = temp;
//如果arr[l]==pivot 前移 r--
if (pivot == array[l]) {
r--;
}
//如果arr[r]==pivot 後移 l++
if (pivot == array[r]) {
l++;
}
}
if (l == r) {
//防止棧溢出
l += 1;
r -= 1;
}
//向左遞歸
if(left<r){
quickSort(array,left,r);
}
//向右遞歸
if(l<right){
quickSort(array,l,right);
}
}
6.桶排序
桶排序 時間複雜度O(n+k) 穩定
* 桶排序思想:將數據 分組放到桶中,遞歸排序桶中的數據, * 當桶中的bucketSize爲1時將數據加入到返回集合中,bucketSize爲大桶中每個小桶的大小 * 當桶中的bucketCount爲1時 將bucketSize的值減1.bucketCount爲大桶中小桶的個數
public static List<Integer> bucketSort(List<Integer> list, int bucketSize){
//遞歸的出口
if(list==null||list.size()<2){
return list;
}
//遍歷list找到最大與最小的值
int max=list.get(0);
int min=list.get(0);
for (int num: list) {
if(num<min){
min=num;
}
if(num>max){
max=num;
}
}
//定義返回結果集
List<Integer> retList = new ArrayList<Integer>();
//求出bucketCount
int bucketCount=(max-min)/bucketSize+1;
//定義大桶
List<List<Integer>> buckets = new ArrayList<>(bucketCount);
//初始化小桶
for (int i = 0; i < bucketCount; i++) {
buckets.add(new ArrayList<>());
}
//遍歷原始的集合將數據放入到桶中
for (int i = 0; i < list.size(); i++) {
int value=list.get(i);
int index=(value-min) /bucketSize;
buckets.get(index).add(value);
}
//依次將桶中的數據進行排序
for (int i = 0; i < buckets.size(); i++) {
if(bucketSize==1){
//說明要麼只有一個元素,要麼桶中的元素都相同
for (int j = 0; j < buckets.get(i).size(); j++) {
retList.add(buckets.get(i).get(j));
}
}else {
if(bucketCount==1){
bucketSize--;
}
//當bucketSize不等於1說明存在兩個以上不同種類的數據
//將小桶中的集合看成原集合再次遍歷
List<Integer> temp = bucketSort(buckets.get(i), bucketSize);
//依次將遞歸返回的集合存放到結果集中
for (Integer num : temp) {
retList.add(num);
}
}
}
return retList;
}
7.歸併排序
歸併排序 時間複雜度O(n log n) 穩定
* 歸併排序介紹:一開始先把數組從中間劃分成兩個子數組,一直遞歸地把子數組劃分成跟小的數組, * 直到子數組裏面只有一個元素,纔開始排序 * 歸併排序思想:把一個複雜問題分成兩個或多個相同或相似的子問題,然後把子問題分成更小的子問題, * 直到子問題可以簡單的直接請求,最原問題的解就是子問題的合併, * 歸併排序將分治的思想體現得淋漓盡致
public static void mergeSort(Integer[] array, int lo, int hi){
//判斷是否只剩下最後一個元素
if(lo>=hi){
return;
}
//從中間將數組分成兩部分
int mid=lo+(hi-lo)/2;
//分別遞歸的將左右兩半排好序
mergeSort(array,lo,mid);
mergeSort(array,mid+1,hi);
//將排好序的兩半合併
merge(array,lo,mid,hi);
}
private static void merge(Integer[] array, int lo, int mid, int hi) {
//複製一份原來的數組
Integer[] copy=array.clone();
//定義k指針表示從什麼位置開始修改原來的數組
int k=lo;
//i指針表示左半邊的起始位置
int i=lo;
//j指針表示右半邊的起始位置
int j=mid+1;
while (k<hi){
if(i>mid){
//當左半邊的數都處理完畢,只需要將右半邊的數逐個拷貝過去
array[k++]=copy[j++];
}else if(j>hi){
//當右半邊的數都處理完畢,只需要將左半邊的數逐個拷貝過去
array[k++]=copy[i++];
}else if(copy[j]<copy[i]){
//當右半邊的數小於左半邊的數,將右半邊的數拷貝,j指針向後移動一位
array[k++]=copy[j++];
}else {
//當左半邊的數小於右半邊的數,將左半邊的數拷貝,i指針向後移動一位
array[k++]=copy[i++];
}
}
}