diff --git a/.vscode/settings.json b/.vscode/settings.json index 2562181..86f533f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,6 +16,10 @@ "exception": "cpp", "fstream": "cpp", "map": "cpp", - "utility": "cpp" + "utility": "cpp", + "ios": "cpp", + "istream": "cpp", + "ostream": "cpp", + "xiosbase": "cpp" } } \ No newline at end of file diff --git a/Algorithm/BubbleSort.h b/Algorithm/BubbleSort.h index c1eda04..9960d4f 100644 --- a/Algorithm/BubbleSort.h +++ b/Algorithm/BubbleSort.h @@ -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& 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]); + } + } + } } \ No newline at end of file diff --git a/Algorithm/BubbleSort_orderly.h b/Algorithm/BubbleSort_orderly.h deleted file mode 100644 index a5fcd88..0000000 --- a/Algorithm/BubbleSort_orderly.h +++ /dev/null @@ -1,14 +0,0 @@ -// 冒泡排序(改进版) -void BubbleSort_orderly(vector& 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]); - } - } - } -} \ No newline at end of file diff --git a/Algorithm/BucketSort.h b/Algorithm/BucketSort.h new file mode 100644 index 0000000..9ed7e90 --- /dev/null +++ b/Algorithm/BucketSort.h @@ -0,0 +1,78 @@ +#include +#include +#include +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 buckets(BUCKET_NUM,(ListNode*)(0)); + for(int i=0;imData; + head = head->mNext; + } +} \ No newline at end of file diff --git a/Algorithm/CountSort.h b/Algorithm/CountSort.h new file mode 100644 index 0000000..4eba4ce --- /dev/null +++ b/Algorithm/CountSort.h @@ -0,0 +1,50 @@ +/***************** + +计数排序基于一个假设,待排序数列的所有数均出现在(0,k)的区间之内,如果k过大则会引起较大的空间复杂度 +计数排序并非是一种基于比较的排序方法,它直接统计出键值本应该出现的位置 +时间复杂度为O(n),空间复杂度为O(n+k) + +*****************/ + + +#include +#include + +using namespace std; + +void countSort(vector& vec,vector& objVec) +{ + vector range(10,0); //range的下标即键值 + for(int i=0;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 vec(a,a+14); + vector objVec(14,0); + + countSort(vec,objVec); + + for(int i=0;i +#include +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; +} \ No newline at end of file diff --git a/Algorithm/InsertSort.h b/Algorithm/InsertSort.h new file mode 100644 index 0000000..c001fb3 --- /dev/null +++ b/Algorithm/InsertSort.h @@ -0,0 +1,31 @@ +/* + +插入排序思路: +1. 从第一个元素开始,该元素可以认为已经被排序 +2. 取出下一个元素,在已经排序的元素序列中从后向前扫描 +3. 如果该元素(已排序)大于新元素,将该元素移到下一位置 +4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置 +5. 将新元素插入到该位置后 +6. 重复步骤2~5 + +*/ + +// 插入排序 +void InsertSort(vector& 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; + } + } +} + diff --git a/Algorithm/MergeSort.h b/Algorithm/MergeSort.h new file mode 100644 index 0000000..5affc43 --- /dev/null +++ b/Algorithm/MergeSort.h @@ -0,0 +1,62 @@ +/***************** + 迭代版 +*****************/ +//整數或浮點數皆可使用,若要使用物件(class)時必須設定"小於"(<)的運算子功能 +template +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 +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 +void merge_sort(T arr[], const int len) { + T *reg = new T[len]; + merge_sort_recursive(arr, reg, 0, len - 1); + delete[] reg; +} \ No newline at end of file diff --git a/Algorithm/RadixSort.h b/Algorithm/RadixSort.h new file mode 100644 index 0000000..5681cc1 --- /dev/null +++ b/Algorithm/RadixSort.h @@ -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; +} \ No newline at end of file diff --git a/Algorithm/ShellSort.h b/Algorithm/ShellSort.h new file mode 100644 index 0000000..e332ec4 --- /dev/null +++ b/Algorithm/ShellSort.h @@ -0,0 +1,15 @@ +template +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; + } +} \ No newline at end of file diff --git a/README.md b/README.md index 1466af6..65bf1f4 100644 --- a/README.md +++ b/README.md @@ -1245,14 +1245,26 @@ typedef struct BiTNode ### 排序 -排序算法 | 平均时间复杂度 | 最差时间复杂度 | 空间复杂度 | 稳定性 ----|---|---|---|--- -[冒泡排序](Algorithm/BubbleSort.h) | O(n2)|O(n2)|O(1)|稳定 -[冒泡排序(改进版)](Algorithm/BubbleSort_orderly.h) | O(n2)|O(n2)|O(1)|稳定 -[选择排序](Algorithm/SelectionSort.h) | O(n2)|O(n2)|O(1)|稳定 -[快速排序](Algorithm/QuickSort.h) | O(n*log2n) | O(n2) | O(log2n)~O(n) | 不稳定 +排序算法 | 平均时间复杂度 | 最差时间复杂度 | 空间复杂度 | 数据对象稳定性 | 描述 +---|---|---|---|---|--- +[冒泡排序](Algorithm/BubbleSort.h) | O(n2)|O(n2)|O(1)|稳定|(无序区,有序区)。从无序区通过交换找出最大元素放到有序区前端。 +[选择排序](Algorithm/SelectionSort.h) | O(n2)|O(n2)|O(1)|数组不稳定、链表稳定|(有序区,无序区)。在无序区里找一个最小的元素跟在有序区的后面。对数组:比较得多,换得少。 +[插入排序](Algorithm/InsertSort.h) | O(n2)|O(n2)|O(1)|稳定|(有序区,无序区)。把无序区的第一个元素插入到有序区的合适的位置。对数组:比较得少,换得多。 +[快速排序](Algorithm/QuickSort.h) | O(n*log2n) | O(n2) | O(log2n) | 不稳定|(小数,基准元素,大数)。在区间中随机挑选一个元素作基准,将小于基准的元素放在基准之前,大于基准的元素放在基准之后,再分别对小数区与大数区进行排序。 +[堆排序](Algorithm/HeapSort.h) | O(n*log2n)|O(n2)|O(1)|不稳定|(最大堆,有序区)。从堆顶把根卸出来放在有序区之前,再恢复堆。 +[归并排序](Algorithm/MergeSort.h) | O(n*log2n) | O(n*log2n)|O(1)|稳定|把数据分为两段,从两段中逐个选最小的元素移入新数据段的末尾。可从上到下或从下到上进行。 +[希尔排序](Algorithm/ShellSort.h) | O(n*log2n)|O(n2)|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(n2)| |稳定|一种多关键字的排序算法,可用桶排序实现。 [文件排序](Algorithm/FileSort) | +> * 均按从小到大排列 +> * k:代表数值中的"数位"个数 +> * n:代表数据规模 +> * m:代表数据的最大值减最小值 +> * 来自 [wikipedia . 排序算法](https://zh.wikipedia.org/wiki/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95) + ### 查找 查找算法 | 平均时间复杂度 | 空间复杂度 | 查找条件