排序算法C++實現
須知
算法使用模板進行編寫,有1個錯誤的排查並沒有完成,我會標註在代碼上的
遞歸——順序查找算法
因爲代碼十分簡短,故沒有對代碼邏輯進行描述,遞歸就行了
template<class T>
int sequential_search(T a[], int n,const T&x)
{
if (n < 1)return -1;
if (a[n - 1] == x)return n - 1;
else return sequential_search(a, n - 1, x);
}
等級排序
這個算法的思想就是a[]爲輸入的亂序數組,n爲元素數量,r[]用來裝載數組a中每個元素在數組中的大小順序,但沒有對a進行排序移位,下邊還有一段被註銷的代碼,那個算法的效率是比上邊算法效率低的,然後下邊的rear_range函數就是用來對數組a依據數組r進行重排序的,n依然爲數組大小
//template<class T>
//a[]原本是T類的聲明
void rank_(int a[], int n, int r[])
{
for (int i = 0; i < n; i++)
{
r[i] = 0;
}//初始化
for (int i = 1; i < n; i++)
{
for (int j = 0; j < i; j++)
{
if (a[j] <= a[i])
r[i]++;
else
r[j]++;
}
}
//j從0開始,爲內循環,當j=i時停止內循環
//i從1開始,每一個a[i]都和它前邊的i個數比較,大的順序自增,如a中第二個三,達成一次自增,則順序爲1
//如果出現後邊還有小的數,如4,3,9,3,7,2
//第一個3和前邊的4對比,4的序號加一,輪到2和4,3,9,3,7對比時,它們全增一
//其實這就是一個排序
//不同於書上的排序
/*
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (a[j] >= a[i])
r[j]++;
else
r[i]++;
}
}
//此處j也進行n次循環,則r中最小的數會爲2,而且是兩個2
//每一個a[i]都和所有的a對比,會出現和自己對比
//自增一次,和另一個相同的數對比,自增一次,結果即2
//故當數組內沒有相同的數時最小的排序值爲1
//出現兩個相同的數時則爲2,以後同理,用j<i作爲條件可以避免
//每一個a[i]只和它前邊的對比,不會和自身對比故從零自增
//後邊相同的數和自己對比出現if中的條件
//然後增的是後來的,不是內循環中的,故還能排序先出和後出現
*/
template<class T>
void rear_range(T a[], int n, int r[])
{
T*u[] = new T[n];
for (int i = 0; i < n; i++)
u[r[i]] = a[i];
//把a中的數按照r得到的名次進行排序
//即4去3號位,3去1號位,9去5號位,3去2號位,7去4號位
for (int i = 0; i < n; i++)
a[i] = u[i];
delete[]u;
}
}
最大值函數和交換函數
實際上進行排序的時候,加入一些簡單的工具性函數更高效(寫代碼的時候)
int index_of_max(T a[], int n)
{//找最大值函數,n爲數組前n個數
if (n <= 0)
{
cout << "error!" << endl;
return - 1;
}
//從前n-1個數中找到最大的數,進行n-1次比較
int indexofmax = 0;
for (int i = 1; i < n; i++)
if (a[indexofmax] < a[i])
indexofmax = i;
return indexofmax;
}
void swap(int&a,int&b)
{
int temp = a;
a = b;
b = temp;
}
選擇排序算法1
這個算法比較簡單暴力,很容易懂,找到最大的,放到最後,然後不管了,開始循環這樣的操作
template<class T>
void selection_sort(T a[], int n)
{//選擇排序
for (int i = n - 1; i > 0; i--)
{
int j = index_of_max(a, i);
//得到前i個數中最大的值的位置,下一次i自減
swap(a[i], a[j]);
//交換第i+1和最大值數的位置
//此選擇排序執行次數每次都不會變,即總爲n-1次
}
}
選擇排序算法2
這個算法設置了flag,可以用來減少排序次數,並且每次排序的時候會檢查整個數組是否完成排序,可以算是優化了吧
template<class T>
void selectionSort(T a[], int n)
{
bool sorted = false;
for (int size = n; !sorted && (size > 0); size--)
{//從後往前放,從大放到小
int index_of_max = 0;
//不管最大的一開始是哪個,先從第一個開始
sorted = true;
//如果沒有出現逆序,就停止最外層循環
for (int i = 0; i < size; i++)
{//不管已經排序的部分,減少排序次數
if (a[index_of_max] < a[i])index_of_max = i;
//若出現當前index錯誤則糾正
else sorted = false;
//若內循環的全部次數裏出現了一次index錯誤即表示沒有排序完,需要再進行排序
}
swap(a[index_of_max], a[size - 1]);
//對當前未排序的部分的最後一個數進行排序
}
}
冒泡算法
相鄰比較,兩個相鄰的數大的向右移動,小的向左移動,就算冒個泡,冒泡次數很明顯是不會變化的
//template<class T>
void bubble(int a[], int n)
{//把a[0:n-1]中最大元素移到最右邊
for (int i = 0; i < n - 1; i++)
if (a[i] > a[i + 1])
swap(a[i], a[i + 1]);
}
//template<class T>
void bubble_sort(int a[], int n)
{
for (int i = n; i > 1; i--)
bubble(a, i);
}
及時終止的冒泡算法
變化在哪?冒泡算法用了bool型,設立了flag,如果一次冒泡中後邊的數存在已完成排序的情況,由flag表示冒泡完成,減少的排序次數應該是在冒泡排序算法上,如果某一次冒泡時已經是順序了,就不用冒泡
template<class T>
bool bubble(T a[], int n)
{
//及時終止的冒泡
bool swapped = false;//先設置一個flag爲false
for (int i = 0; i <= n - 1; i++)
{
//循環n次
if (a[i] > a[i + 1])
{
swap(a[i], a[i + 1]);//傳統冒泡行爲
swapped = true;//如果存在冒泡行爲即表示沒有完成排序
}
}
return swapped;
}
template<class T>
void bubble_sort(T a[], int n)
{
for (int i = n - 1; i > 0 && bubble(a, i); i--);
//最多進行n次循環,如果存在沒有冒泡行爲就停止
//即當bubble爲false,即完成排序,即可省略之後的循環次數
}
插入排序1
插入排序總共有三個算法,這是第一個
template<class T>
void insert0(T a[], int& n, const T&x)
{
//插入排序即在有序數組中插入一個元素
//其中n爲數組中元素個數,並不是數組最大容量
//數組最後一個數爲a[n-1]
int i;//i需申明在循環以外,否則會報錯的
for (i = n - 1; i >= 0 && x < a[i]; i--)
a[i + 1] = a[i];//比x大的數往後移
a[i + 1] = x;
//跳出循環即x>=a[i],則有x應當處於a[i]後邊一位
n++;//總數加一
}
插入排序2
插入排序總共有三個算法,這是第二個
template<class T>
void insert(T a[], int& n, const T&x)
{//插入排序即在有序數組中插入一個元素
//其中n作爲最後一個數的標誌,即把x插入有序數組a[0:n-1]中
//下邊用循環中變化的i表示有序數組的大小
//其中n爲數組中元素個數,並不是數組最大容量
//數組最後一個數爲a[n-1]
int i;//i需申明在循環以外
for (i = n - 1; i >= 0 && x < a[i]; i--)
a[i + 1] = a[i];//比x大的數往後移
a[i + 1] = x;
//跳出循環即x>=a[i]
//則有x應當處於a[i]後邊一位
}
template<class T>
void insert_sort(T a[], int n)
{
for (int i = 1; i < n; i++)
{
T t = a[i];
//獲得當前最後一個未排序的數值
insert(a, i, t);
//就像放麻將一樣,拿起最後未排序的數放入應該放的位置
//所有比它大的數提前後移,這是插入與放麻將的一點區別
}
}
插入排序3
插入排序總共有三個算法,這是第三個
template<class T>
void insertionsort(T a[],int n)
{
for (int i = 1; i < n; i++)
//內循環決定從後向前進行對比,則外循環需從零開始增加
{
T t = a[i];//當前未排序的數的值
int j;
for (j = i-1; j >=0 && (t < a[j]);j--)
//①前面一個數跟後面一個數對比,從後向前比
//只有當前未排序的數小於以此遇到的數才能繼續循環
a[j+1]=a[j];
//②因爲已經用t寄存當前插入數值,故可視後邊一個位置爲空
//各個比當前數值大的數都後移,以留出當前數值的位置
a[j + 1] = t;
//③此時j記錄的是比當前數值小的數的位置
//因爲當前數值要放在該位置之後一位,故用j+1
}
}
歸併排序(正確)
這是網上借鑑來的一個算法,我用三目運算符簡化了一下代碼
//對已排好的兩個數組進行合併
void merge(int *arr, int low, int mid, int high)
{//從數組arr[low:high]
int i, j, count = 0;
int *data = new int[high - low + 1];
for (i = low, j = mid + 1; i <= mid && j <= high; count++)data[count] = (arr[i] < arr[j]) ? arr[i++] : arr[j++];
//進行循環直到for循環裏的條件被破壞,則一定會在下列兩種情況中選出一種
for (; i <= mid;count++,i++)data[count] = arr[i];
for (; j <= high;count++,j++)data[count] = arr[j];
i = low;
for (int k = 0; k < count&&i <= high; k++, i++)arr[i] = data[k];
delete[] data;
}
//分而治之,將長數組一直進行二等分,當分到只剩一個時返回
void Msort(int* arr, int low, int high)
{
if (low >= high){return;}
int mid = (low + high) / 2;
Msort(arr, low, mid);
Msort(arr, mid + 1, high);
merge(arr, low, mid, high);
}
歸併排序(故障)
這個算法來自《無處不在的算法》機械工業出版社,原本好像是java寫的,我用c++改了一下,但改來改去都不能出正確結果,有大佬會改的話求指教!我是真的盡力從理論上在修改過了一個晚上,但是奈何…其實放在這裏既算是一個記錄,也是一個求助
template<class T>
void merge(T a[], int al, int ar, T b[], int bl, int br, T c[])
{
//用三目運算符在ar和br之前把小的數放入c中,遇到超出ar或br就放沒超出的那個,執行次數爲兩個數組之和
//該函數用於把兩個有序的數組合併到一起
int i = al, j = bl;
for (int k = 0; k < ar - al + br - bl + 1; k++)
{
if (i < ar&&j < br)c[k] = (a[i] < b[j]) ? a[i++] : b[j++];
if (i > ar)c[k] = b[j++];
if (j > br)c[k] = a[i++];
}
}
template<class T>
void merge_sort(T a[], int al, int ar)
{
if (ar > al)
{
int m = (ar + al) / 2;//找到中間點
merge_sort(a, al, m);
//用遞推法把a在m的左右逐步分爲最小個數,然後執行下方排序
merge_sort(a, m + 1, ar);
T*b = new T[ar - al + 1];
//聲明一個新數組臨時存儲有序的a
merge(a, al, m, a, m + 1, ar, b);
//把有序的數逐步合起來
for (int i = 0; i < ar - al + 1; i++)
a[al + i] = b[i];
}
}
快速排序
代碼裏含有用於觀察數字如何變化的cout代碼,都已經註釋掉了,可以直接用噠
void quickSort(int *A, int al, int ar)
{
if (al < ar)
{
int pivot = A[al];
int i = al, j = ar + 1;
while (true)
{
while (A[++i] > pivot&&i < ar)
{
//cout << "達成 i 的移動" << endl;
};
while (A[--j] < pivot&&j > al)
{
//cout << "達成 j 的移動" << endl;
};
if (i < j)
{
//cout << "交換" << A[i] << " 和 " << A[j] << endl;
swap(A[i], A[j]);
}
else
break;
}
//cout << endl << "還未移動pivot,此時已完成除pivot以外的所有數的移動,當前數組順序如下:" << endl;
//for (int k = 0; k < ar - al + 1; k++)
//cout << A[k] << " ";
//cout << endl;
//cout << " j= " << j <<" i= "<<i<< endl;
//上方步驟用來把pivot兩邊的數值堆起來,兩邊的裏層順序並沒有排序
swap(A[al], A[j]);
//此步驟用於把pivot放到它應該在的位置
//之後就是把pivot的左右兩邊用遞歸來排序的,也是再找各自的左邊第一個數爲pivot,然後把pivot再放到正確位置,再對這個pivot的左右進行排序,然後迴歸到最外層
quickSort(A, al, j - 1);
quickSort(A, j + 1, ar);
}
}