冒泡排序
/*
(無序區,有序區)。從無序區通過交換找出最大元素放到有序區前端。
選擇排序思路:
1. 比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
2. 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。這步做完後,最後的元素會是最大的數。
3. 針對所有的元素重複以上的步驟,除了最後一個。
4. 持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
*/
// 冒泡排序
void BubbleSort(vector<int>& v) {
int len = v.size();
for (int i = 0; i < len - 1; ++i)
for (int j = 0; j < len - 1 - i; ++j)
if (v[j] > v[j + 1])
swap(v[j], v[j + 1]);
}
// 冒泡排序(改進版)
void BubbleSort_orderly(vector<int>& v) {
int len = v.size();
bool orderly = false;
for (int i = 0; i < len - 1 && !orderly; ++i) {
orderly = true;
for (int j = 0; j < len - 1 - i; ++j) {
if (v[j] > v[j + 1]) { // 從小到大
orderly = false; // 發生交換則仍非有序
swap(v[j], v[j + 1]);
}
}
}
}
選擇排序
/*
(有序區,無序區)。在無序區裏找一個最小的元素跟在有序區的後面。對數組:比較得多,換得少。
選擇排序思路:
1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
2. 從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾
3. 以此類推,直到所有元素均排序完畢
*/
// 選擇排序
void SelectionSort(vector<int>& v) {
int min, len = v.size();
for (int i = 0; i < len - 1; ++i) {
min = i;
for (int j = i + 1; j < len; ++j) {
if (v[j] < v[min]) { // 標記最小的
min = j;
}
}
if (i != min) // 交換到前面
swap(v[i], v[min]);
}
}
插入排序
i,j
start+diff,i
n,start
小小互換
+-diff
/*
(有序區,無序區)。把無序區的第一個元素插入到有序區的合適的位置。對數組:比較得少,換得多。
插入排序思路:
1. 從第一個元素開始,該元素可以認爲已經被排序
2. 取出下一個元素,在已經排序的元素序列中從後向前掃描
3. 如果該元素(已排序)大於新元素,將該元素移到下一位置
4. 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置
5. 將新元素插入到該位置後
6. 重複步驟2~5
*/
// 插入排序
void InsertSort(vector<int>& v){
int start=0;
int diff=1;
for (int i = start+diff; i < v.size(); i+=diff)
for(int j = i; j>start&&v[j]<v[j-diff]; j-=diff)
swap(v[j],v[j-diff]);
}
希爾排序
diff=n/3;diff>=1;diff/=3
start:0~diff-1
插入排序
// 希爾排序:每一輪按照事先決定的間隔進行插入排序,間隔會依次縮小,最後一次一定要是1。
const int INCRGAP = 3;
void shellsort(vector<int> &v){
int n=v.size();
for(int diff= n / INCRGAP;diff> 0;diff/= INCRGAP)//遍歷所有增量大小
for(int start = 0;start < diff;start++){
/*對子序列進行插入排序,當增量爲1時,對所有元素進行最後一次插入排序*/
for (int i = start+diff; i < v.size(); i+=diff)
for(int j = i; j>start&&v[j]<v[j-diff]; j-=diff)
swap(v[j],v[j-diff]);
快速排序
data[left++]<=key<=data[right--]
while(left<right) if(left<right)
data[right--]=data[left]
data[left++]=data[right]
/*
(小數,基準元素,大數)。在區間中隨機挑選一個元素作基準,將小於基準的元素放在基準之前,大於基準的元素放在基準之後,再分別對小數區與大數區進行排序。
快速排序思路:
1. 選取第一個數爲基準
2. 將比基準小的數交換到前面,比基準大的數交換到後面
3. 對左右區間重複第二步,直到各區間只有一個數
*/
// ----------------------------------------------------
// 快速排序(遞歸)
void QuickSort(vector<int>& v, int low, int high) {
if (low >= high)
return;
int key = v[low];// 也可以寫個取v[low],v[high],v[(low+high)>>1]中位數做基準
/*另一種寫法
int first=low-1;
int key=nums[high];
for(int last=low;last<ri;++last){
if(nums[last]<=key)
swap(nums[last],nums[++first]);
}
swap(nums[high],nums[++first]);
*/
int first = low;
int last = high;
while (first < last){
while (first < last && v[last] >= key)
last--;
if (first < last)
v[first++] = v[last];
while (first < last && v[first] <= key)
first++;
if (first < last)
v[last--] = v[first];
}
v[first] = key;
QuickSort(v, low, first - 1);
QuickSort(v, first + 1, high);
}
堆排序
son=dad<<1+1;
兒子和兒子比,兒子和爸比,互換兒爸接着比
n/2-1~0開始建堆
0~(n-1~1)開始取最大值置後
// 堆排序:(最大堆,有序區)。從堆頂把根卸出來放在有序區之前,再恢復堆。
void max_heapify(int arr[], int start, int end) {
//建立父節點指標和子節點指標
int dad = start;
int son = dad * 2 + 1;
while (son <= end) { //若子節點指標在範圍內才做比較
if (son + 1 <= end && arr[son] < arr[son + 1]) //先比較兩個子節點大小,選擇最大的
son++;
if (arr[dad] > arr[son]) //如果父節點大於子節點代表調整完畢,直接跳出函數
return;
else { //否則交換父子內容再繼續子節點和孫節點比較
swap(arr[dad], arr[son]);
dad = son;
son = dad * 2 + 1;
}
}
}
void heap_sort(vector<int>arr) {
len=arr.size();
//初始化,i從最後一個父節點開始調整
for (int i = len / 2 - 1; i >= 0; i--)
max_heapify(arr, i, len - 1);
//先將第一個元素和已經排好的元素前一位做交換,再從新調整(剛調整的元素之前的元素),直到排序完畢
for (int i = len - 1; i > 0; i--) {
swap(arr[0], arr[i]);
max_heapify(arr, 0, i - 1);
}
}
歸併排序
二分
遞歸後序
data[a,b]+data[b+1,c]->temp[ ]->data[a,c]
// 歸併排序:把數據分爲兩段,從兩段中逐個選最小的元素移入新數據段的末尾。可從上到下或從下到上進行。
/*****************
迭代版
*****************/
//整數或浮點數皆可使用,若要使用物件(class)時必須設定"小於"(<)的運算子功能
template<typename T>
void merge_sort(T arr[], int len) {
T* a = arr;
T* b = new T[len];
for (int seg = 1; seg < len; seg += seg) {
for (int start = 0; start < len; start += seg + seg) {
int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len);
int k = low;
int start1 = low, end1 = mid;
int start2 = mid, end2 = high;
while (start1 < end1 && start2 < end2)
b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
while (start1 < end1)
b[k++] = a[start1++];
while (start2 < end2)
b[k++] = a[start2++];
}
T* temp = a;
a = b;
b = temp;
}
if (a != arr) {
for (int i = 0; i < len; i++)
b[i] = a[i];
b = a;
}
delete[] b;
}
/*****************
遞歸版
*****************/
template<typename T>
void merge_sort_recursive(T arr[], T reg[], int start, int end) {
if (start >= end)
return;
int len = end - start, mid = (len >> 1) + start;
int start1 = start, end1 = mid;
int start2 = mid + 1, end2 = end;
merge_sort_recursive(arr, reg, start1, end1);
merge_sort_recursive(arr, reg, start2, end2);
int k = start;
while (start1 <= end1 && start2 <= end2)
reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
while (start1 <= end1)
reg[k++] = arr[start1++];
while (start2 <= end2)
reg[k++] = arr[start2++];
for (k = start; k <= end; k++)
arr[k] = reg[k];
}
//整數或浮點數皆可使用,若要使用物件(class)時必須設定"小於"(<)的運算子功能
template<typename T>
void merge_sort(T arr[], const int len) {
T *reg = new T[len];
merge_sort_recursive(arr, reg, 0, len - 1);
delete[] reg;
}
計數排序
temp(n)
count(max_element+1)
置零+計數+求和+逆序賦值
/*****************
計數排序:統計小於等於該元素值的元素的個數i,於是該元素就放在目標數組的索引i位(i≥0)。
計數排序基於一個假設,待排序數列的所有數均爲整數,且出現在(0,k)的區間之內。
如果 k(待排數組的最大值) 過大則會引起較大的空間複雜度,一般是用來排序 0 到 100 之間的數字的最好的算法,但是它不適合按字母順序排序人名。
計數排序不是比較排序,排序的速度快於任何比較排序算法。
時間複雜度爲 O(n+k),空間複雜度爲 O(n+k)
算法的步驟如下:
1. 找出待排序的數組中最大和最小的元素
2. 統計數組中每個值爲 i 的元素出現的次數,存入數組 C 的第 i 項
3. 對所有的計數累加(從 C 中的第一個元素開始,每一項和前一項相加)
4. 反向填充目標數組:將每個元素 i 放在新數組的第 C[i] 項,每放一個元素就將 C[i] 減去 1
*****************/
// 計數排序
void CountSort(vector<int>& data){//對比基數排序
int n=data.size();
vector<int>tmp(n);
int m=max_element(data.begin(),data.end());
vector<int>count(m+1,0); //for(j = 0; j < 10; j++) count[j] = 0;
for(j = 0; j < n; j++)
k = data[j],++count[k];
for(j = 1; j <= m; j++)
count[j] = count[j - 1] + count[j];
for(j = n - 1; j >= 0; j--) {
k = data[j];
tmp[--count[k]] = data[j];
}
data=temp;//for(j = 0; j < n; j++) data[j] = tmp[j];
}
基數排序
最大位數
計數排序(0~9):temp(n)+count(9+1)
data[i]除1/10/100/...再模10 得0~9
// 基數排序:一種多關鍵字的排序算法,可用桶排序實現。
int maxbit(vector<int> &data) //輔助函數,求數據的最大位數{
int maxData = max_element(data.begin(),date.end());
int d = 1;
while (maxData >= 10)
maxData /= 10,++d;
return d;
}
void radixsort(vector<int> &data) //基數排序{
int n=data.size();
int d = maxbit(data);
vector<int>tmp(n);
vector<int>count(10);
int i, j, k;
int radix = 1;
for(i = 1; i <= d; i++,radix = radix * 10) //進行d次排序
{
for(j = 0; j < 10; j++)
count[j] = 0;
for(j = 0; j < n; j++)
k = (data[j] / radix) % 10,++count[k];
for(j = 1; j < 10; j++)
count[j] = count[j - 1] + count[j];
for(j = n - 1; j >= 0; j--) {
k = (data[j] / radix) % 10;
tmp[--count[k]] = data[j];
}
for(j = 0; j < n; j++) //將臨時數組的內容複製到data中
data[j] = tmp[j];
}
}
桶排序
int bucketsNum = (max - min)/100 + 1;//桶數
vector<list<int>> buckets(bucketsNum);//數據結構
insert(buckets[(value-min)/k],value);//list有序插入
void insert(list<int>& bucket,int val){
auto iter = bucket.begin();
while(iter != bucket.end() && val >= *iter) ++iter;
bucket.insert(iter,val);
}
void BucketSort_1(vector<int>& arr)
{
int len = arr.size();
if(len <= 1)
return;
int min = min_element(arr.begin(),arr.end());
int max = max_element(arr.begin(),arr.end());
int k = 10;//k爲數字之間的間隔
//向上取整,例如[0,9]有10個數,(9 - 0)/k + 1 = 1;
int bucketsNum = (max - min)/k + 1;
vector<list<int>> buckets(bucketsNum);
for(int i=0;i<len;++i)
{
int value = arr[i];
//(value-min)/k就是在哪個桶裏面
insert(buckets[(value-min)/k],value);
}
int index = 0;
for(int i=0;i<bucketsNum;++i)
{
if(buckets[i].size())
{
for(auto& value:buckets[i])
arr[index++] = value;
}
}
}
int main()
{
vector<int> A={-100,13,14,94,33,82,25,59,94,65,23,45,27,43,25,39,10,35,54,90,-200,58};
for(auto value:A)
cout<<value<<" ";
cout<<endl;
BucketSort_1(A);
for(auto value:A)
cout<<value<<" ";
cout<<endl;
return 0;
}