mirror of
https://github.com/huihut/interview.git
synced 2024-03-22 13:10:48 +08:00
添加插入、堆、归并、希尔、计数、桶、基数排序算法及其实现
This commit is contained in:
parent
90ef525b75
commit
3a0079899d
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
|
@ -16,6 +16,10 @@
|
|||
"exception": "cpp",
|
||||
"fstream": "cpp",
|
||||
"map": "cpp",
|
||||
"utility": "cpp"
|
||||
"utility": "cpp",
|
||||
"ios": "cpp",
|
||||
"istream": "cpp",
|
||||
"ostream": "cpp",
|
||||
"xiosbase": "cpp"
|
||||
}
|
||||
}
|
|
@ -24,4 +24,19 @@ void bubble_sort(T arr[], int len) {
|
|||
for (int j = 0; j < len - 1 - i; j++)
|
||||
if (arr[j] > arr[j + 1])
|
||||
swap(arr[j], arr[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,14 +0,0 @@
|
|||
// 冒泡排序(改进版)
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
78
Algorithm/BucketSort.h
Normal file
78
Algorithm/BucketSort.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include<iterator>
|
||||
#include<iostream>
|
||||
#include<vector>
|
||||
using namespace std;
|
||||
|
||||
/*****************
|
||||
|
||||
桶排序序思路:
|
||||
1. 设置一个定量的数组当作空桶子。
|
||||
2. 寻访序列,并且把项目一个一个放到对应的桶子去。
|
||||
3. 对每个不是空的桶子进行排序。
|
||||
4. 从不是空的桶子里把项目再放回原来的序列中。
|
||||
|
||||
假设数据分布在[0,100)之间,每个桶内部用链表表示,在数据入桶的同时插入排序,然后把各个桶中的数据合并。
|
||||
|
||||
*****************/
|
||||
|
||||
|
||||
const int BUCKET_NUM = 10;
|
||||
|
||||
struct ListNode{
|
||||
explicit ListNode(int i=0):mData(i),mNext(NULL){}
|
||||
ListNode* mNext;
|
||||
int mData;
|
||||
};
|
||||
|
||||
ListNode* insert(ListNode* head,int val){
|
||||
ListNode dummyNode;
|
||||
ListNode *newNode = new ListNode(val);
|
||||
ListNode *pre,*curr;
|
||||
dummyNode.mNext = head;
|
||||
pre = &dummyNode;
|
||||
curr = head;
|
||||
while(NULL!=curr && curr->mData<=val){
|
||||
pre = curr;
|
||||
curr = curr->mNext;
|
||||
}
|
||||
newNode->mNext = curr;
|
||||
pre->mNext = newNode;
|
||||
return dummyNode.mNext;
|
||||
}
|
||||
|
||||
|
||||
ListNode* Merge(ListNode *head1,ListNode *head2){
|
||||
ListNode dummyNode;
|
||||
ListNode *dummy = &dummyNode;
|
||||
while(NULL!=head1 && NULL!=head2){
|
||||
if(head1->mData <= head2->mData){
|
||||
dummy->mNext = head1;
|
||||
head1 = head1->mNext;
|
||||
}else{
|
||||
dummy->mNext = head2;
|
||||
head2 = head2->mNext;
|
||||
}
|
||||
dummy = dummy->mNext;
|
||||
}
|
||||
if(NULL!=head1) dummy->mNext = head1;
|
||||
if(NULL!=head2) dummy->mNext = head2;
|
||||
|
||||
return dummyNode.mNext;
|
||||
}
|
||||
|
||||
void BucketSort(int n,int arr[]){
|
||||
vector<ListNode*> buckets(BUCKET_NUM,(ListNode*)(0));
|
||||
for(int i=0;i<n;++i){
|
||||
int index = arr[i]/BUCKET_NUM;
|
||||
ListNode *head = buckets.at(index);
|
||||
buckets.at(index) = insert(head,arr[i]);
|
||||
}
|
||||
ListNode *head = buckets.at(0);
|
||||
for(int i=1;i<BUCKET_NUM;++i){
|
||||
head = Merge(head,buckets.at(i));
|
||||
}
|
||||
for(int i=0;i<n;++i){
|
||||
arr[i] = head->mData;
|
||||
head = head->mNext;
|
||||
}
|
||||
}
|
50
Algorithm/CountSort.h
Normal file
50
Algorithm/CountSort.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*****************
|
||||
|
||||
计数排序基于一个假设,待排序数列的所有数均出现在(0,k)的区间之内,如果k过大则会引起较大的空间复杂度
|
||||
计数排序并非是一种基于比较的排序方法,它直接统计出键值本应该出现的位置
|
||||
时间复杂度为O(n),空间复杂度为O(n+k)
|
||||
|
||||
*****************/
|
||||
|
||||
|
||||
#include<iostream>
|
||||
#include<vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
void countSort(vector<int>& vec,vector<int>& objVec)
|
||||
{
|
||||
vector<int> range(10,0); //range的下标即键值
|
||||
for(int i=0;i<vec.size();++i)
|
||||
{//统计每个键值出现的次数
|
||||
range[vec[i]]++;
|
||||
}
|
||||
|
||||
for(int i=1;i<vec.size();++i)
|
||||
{//后面的键值出现的位置为前面所有键值出现的次数之和
|
||||
range[i]+=range[i-1];
|
||||
}
|
||||
//至此,range中存放的是相应键值应该出现的位置
|
||||
int length=vec.size();
|
||||
for(int i=length-1;i>=0;--i) //注意一个小细节,统计时最正序的,这里是逆序
|
||||
{//如果存在相同的键值,为了保持稳定性,后出现的应该还是位于后面
|
||||
//如果正序,则先出现的会放置到后面,因此不再稳定
|
||||
objVec[range[vec[i]]]=vec[i]; //将键值放到目标位置
|
||||
range[vec[i]]--;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int a[14]={0,5,7,9,6,3,4,5,2,8,6,9,2,1};
|
||||
vector<int> vec(a,a+14);
|
||||
vector<int> objVec(14,0);
|
||||
|
||||
countSort(vec,objVec);
|
||||
|
||||
for(int i=0;i<objVec.size();++i)
|
||||
cout<<objVec[i]<<" ";
|
||||
cout<<endl;
|
||||
|
||||
return 0;
|
||||
}
|
41
Algorithm/HeapSort.h
Normal file
41
Algorithm/HeapSort.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include <iostream>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
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(int arr[], int len) {
|
||||
//初始化,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);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };
|
||||
int len = (int) sizeof(arr) / sizeof(*arr);
|
||||
heap_sort(arr, len);
|
||||
for (int i = 0; i < len; i++)
|
||||
cout << arr[i] << ' ';
|
||||
cout << endl;
|
||||
return 0;
|
||||
}
|
31
Algorithm/InsertSort.h
Normal file
31
Algorithm/InsertSort.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
|
||||
插入排序思路:
|
||||
1. 从第一个元素开始,该元素可以认为已经被排序
|
||||
2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
|
||||
3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
|
||||
4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
|
||||
5. 将新元素插入到该位置后
|
||||
6. 重复步骤2~5
|
||||
|
||||
*/
|
||||
|
||||
// 插入排序
|
||||
void InsertSort(vector<int>& v)
|
||||
{
|
||||
int len = v.size();
|
||||
for (int i = 1; i < len - 1; ++i) {
|
||||
int temp = v[i];
|
||||
for(int j = i - 1; j >= 0; --j)
|
||||
{
|
||||
if(v[j] > temp)
|
||||
{
|
||||
v[j + 1] = v[j];
|
||||
v[j] = temp;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
62
Algorithm/MergeSort.h
Normal file
62
Algorithm/MergeSort.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*****************
|
||||
迭代版
|
||||
*****************/
|
||||
//整數或浮點數皆可使用,若要使用物件(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;
|
||||
}
|
65
Algorithm/RadixSort.h
Normal file
65
Algorithm/RadixSort.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*****************
|
||||
基数排序
|
||||
*****************/
|
||||
|
||||
int maxbit(int data[], int n) //辅助函数,求数据的最大位数
|
||||
{
|
||||
int maxData = data[0]; ///< 最大数
|
||||
/// 先求出最大数,再求其位数,这样有原先依次每个数判断其位数,稍微优化点。
|
||||
for (int i = 1; i < n; ++i)
|
||||
{
|
||||
if (maxData < data[i])
|
||||
maxData = data[i];
|
||||
}
|
||||
int d = 1;
|
||||
int p = 10;
|
||||
while (maxData >= p)
|
||||
{
|
||||
//p *= 10; // Maybe overflow
|
||||
maxData /= 10;
|
||||
++d;
|
||||
}
|
||||
return d;
|
||||
/* int d = 1; //保存最大的位数
|
||||
int p = 10;
|
||||
for(int i = 0; i < n; ++i)
|
||||
{
|
||||
while(data[i] >= p)
|
||||
{
|
||||
p *= 10;
|
||||
++d;
|
||||
}
|
||||
}
|
||||
return d;*/
|
||||
}
|
||||
void radixsort(int data[], int n) //基数排序
|
||||
{
|
||||
int d = maxbit(data, n);
|
||||
int *tmp = new int[n];
|
||||
int *count = new int[10]; //计数器
|
||||
int i, j, k;
|
||||
int radix = 1;
|
||||
for(i = 1; i <= d; i++) //进行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]; //将tmp中的位置依次分配给每个桶
|
||||
for(j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中
|
||||
{
|
||||
k = (data[j] / radix) % 10;
|
||||
tmp[count[k] - 1] = data[j];
|
||||
count[k]--;
|
||||
}
|
||||
for(j = 0; j < n; j++) //将临时数组的内容复制到data中
|
||||
data[j] = tmp[j];
|
||||
radix = radix * 10;
|
||||
}
|
||||
delete []tmp;
|
||||
delete []count;
|
||||
}
|
15
Algorithm/ShellSort.h
Normal file
15
Algorithm/ShellSort.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
template<typename T>
|
||||
void shell_sort(T array[], int length) {
|
||||
int h = 1;
|
||||
while (h < length / 3) {
|
||||
h = 3 * h + 1;
|
||||
}
|
||||
while (h >= 1) {
|
||||
for (int i = h; i < length; i++) {
|
||||
for (int j = i; j >= h && array[j] < array[j - h]; j -= h) {
|
||||
std::swap(array[j], array[j - h]);
|
||||
}
|
||||
}
|
||||
h = h / 3;
|
||||
}
|
||||
}
|
24
README.md
24
README.md
|
@ -1245,14 +1245,26 @@ typedef struct BiTNode
|
|||
|
||||
### 排序
|
||||
|
||||
排序算法 | 平均时间复杂度 | 最差时间复杂度 | 空间复杂度 | 稳定性
|
||||
---|---|---|---|---
|
||||
[冒泡排序](Algorithm/BubbleSort.h) | O(n<sup>2</sup>)|O(n<sup>2</sup>)|O(1)|稳定
|
||||
[冒泡排序(改进版)](Algorithm/BubbleSort_orderly.h) | O(n<sup>2</sup>)|O(n<sup>2</sup>)|O(1)|稳定
|
||||
[选择排序](Algorithm/SelectionSort.h) | O(n<sup>2</sup>)|O(n<sup>2</sup>)|O(1)|稳定
|
||||
[快速排序](Algorithm/QuickSort.h) | O(n*log<sub>2</sub>n) | O(n<sup>2</sup>) | O(log<sub>2</sub>n)~O(n) | 不稳定
|
||||
排序算法 | 平均时间复杂度 | 最差时间复杂度 | 空间复杂度 | 数据对象稳定性 | 描述
|
||||
---|---|---|---|---|---
|
||||
[冒泡排序](Algorithm/BubbleSort.h) | O(n<sup>2</sup>)|O(n<sup>2</sup>)|O(1)|稳定|(无序区,有序区)。从无序区通过交换找出最大元素放到有序区前端。
|
||||
[选择排序](Algorithm/SelectionSort.h) | O(n<sup>2</sup>)|O(n<sup>2</sup>)|O(1)|数组不稳定、链表稳定|(有序区,无序区)。在无序区里找一个最小的元素跟在有序区的后面。对数组:比较得多,换得少。
|
||||
[插入排序](Algorithm/InsertSort.h) | O(n<sup>2</sup>)|O(n<sup>2</sup>)|O(1)|稳定|(有序区,无序区)。把无序区的第一个元素插入到有序区的合适的位置。对数组:比较得少,换得多。
|
||||
[快速排序](Algorithm/QuickSort.h) | O(n*log<sub>2</sub>n) | O(n<sup>2</sup>) | O(log<sub>2</sub>n) | 不稳定|(小数,基准元素,大数)。在区间中随机挑选一个元素作基准,将小于基准的元素放在基准之前,大于基准的元素放在基准之后,再分别对小数区与大数区进行排序。
|
||||
[堆排序](Algorithm/HeapSort.h) | O(n*log<sub>2</sub>n)|O(n<sup>2</sup>)|O(1)|不稳定|(最大堆,有序区)。从堆顶把根卸出来放在有序区之前,再恢复堆。
|
||||
[归并排序](Algorithm/MergeSort.h) | O(n*log<sub>2</sub>n) | O(n*log<sub>2</sub>n)|O(1)|稳定|把数据分为两段,从两段中逐个选最小的元素移入新数据段的末尾。可从上到下或从下到上进行。
|
||||
[希尔排序](Algorithm/ShellSort.h) | O(n*log<sup>2</sup>n)|O(n<sup>2</sup>)|O(1)|不稳定|每一轮按照事先决定的间隔进行插入排序,间隔会依次缩小,最后一次一定要是1。
|
||||
[计数排序](Algorithm/CountSort.h) | O(n+m)|O(n+m)|O(n+m)|稳定|统计小于等于该元素值的元素的个数i,于是该元素就放在目标数组的索引i位(i≥0)。
|
||||
[桶排序](Algorithm/BucketSort.h) | O(n)|O(n)|O(m)|稳定|将值为i的元素放入i号桶,最后依次把桶里的元素倒出来。
|
||||
[基数排序](Algorithm/RadixSort.h) | O(k*n)|O(n<sup>2</sup>)| |稳定|一种多关键字的排序算法,可用桶排序实现。
|
||||
[文件排序](Algorithm/FileSort) |
|
||||
|
||||
> * 均按从小到大排列
|
||||
> * k:代表数值中的"数位"个数
|
||||
> * n:代表数据规模
|
||||
> * m:代表数据的最大值减最小值
|
||||
> * 来自 [wikipedia . 排序算法](https://zh.wikipedia.org/wiki/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95)
|
||||
|
||||
### 查找
|
||||
|
||||
查找算法 | 平均时间复杂度 | 空间复杂度 | 查找条件
|
||||
|
|
Loading…
Reference in New Issue
Block a user