前言:这个月开始拜读《算法导论》,难度挺大的,在这里记录一下课后习题的思路和代码实现。
2-1:在归并排序中对小数组用插入排序
分析:在多小的小数组时采用插入排序呢?
归并排序的最坏时间复杂度和平均时间复杂度都是 nlogn
插入排序的最坏时间复杂度为 n(n-1)/2, 平均时间复杂度为 n(n-1)/4
所以另 nlogn = n(n-1)/2 -- <1>
nlogn = n(n-1)/4 -- <2>
n大约在6~18之间,插入排序和归并排序可以说效率几乎相等,
n大于这个范围应该用归并排序,小于这个范围应该用插入排序
此次取n = 10
#include
using namespace std;
void merge(int arr[], int low, int mid, int high) // arr[low, mid] and arr[mid + 1, high] are sorted
{
int* tmp = new int[high - low + 1];
int left = low, right = mid + 1, index = 0;
while (left <= mid && right <= high)
{
if (arr[left] <= arr[right])
{
tmp[index] = arr[left];
++left;
}
else
{
tmp[index] = arr[right];
++right;
}
++index;
}
while (left <= mid)
{
tmp[index] = arr[left];
++left;
++index;
}
while (right <= high)
{
tmp[index] = arr[right];
++index;
++right;
}
for (int i = 0; i < index; ++i, ++low) // dont forget ++low
arr[low] = tmp[i]; // 注意是tmp[i] 不是tmp[index]
delete [] tmp;
}
void insertion_sort(int arr[], int low, int high) // [low, high]
{
if (high - low < 1) return;
for (int i = low + 1; i <= high; ++i)
{
int key = arr[i];
int j = i - 1;
while (j >= low && key < arr[j])
{
arr[j + 1] = arr[j];
--j;
}
arr[j + 1] = key;
}
}
void merge_and_insert_sort(int arr[], int low, int high) // [low, high]
{
if (high - low >= 10) // 当元素个数大于10的时候,用归并排序
{
int mid = (low + high) / 2;
merge_and_insert_sort(arr, low, mid);
merge_and_insert_sort(arr, mid + 1, high);
merge(arr, low, mid, high);
}
else // 当元素个数小于等于10的时候,用插入排序
{
insertion_sort(arr, low, high);
}
}
int main()
{
int arr[20] = {1,9,20,5,7,13,19,15,14,3,17,16,2,11,8,4,18,12,10,6};
cout << "prim: ";
for (int i = 0; i < 20; ++i) cout << arr[i] << " "; cout << endl;
merge_and_insert_sort(arr, 0, 19);
cout << "mais: ";
for (int i = 0; i < 20; ++i) cout << arr[i] << " "; cout << endl;
return 0;
}
2.1-2 重写INSERTION_SORT,使之按非升序排列
#include
#include
using namespace std;
void Insertion_sort_lower(vector& iv)
{
for (int i = 1; i < iv.size(); ++i)
{
int key = iv[i];
int j = i - 1;
while (j >= 0 && iv[j] < key)
{
iv[j + 1] = iv[j];
--j;
}
iv[j + 1] = key;
}
}
int main()
{
vector v = {5, 3, 9, 7, 10, 1, 4, 7, 0, 2, 8};
cout << "原 序 列:";
for (auto i : v) cout << i << " ";
cout << endl;
Insertion_sort_lower(v);
cout << "排序后序列:";
for (auto i : v) cout << i << " ";
cout << endl;
return 0;
}
2.1-3 题目:
输入:一个序列,一个值v
输出:使得v = a[i]的下标i,若v不在a中出现,则v为特殊值NIL
#include
#include
#include
using namespace std;
int main()
{
vector sv;
string s;
while (cin >> s && s != "END")
sv.push_back(s);
string v;
cin >> v;
int flag = 0; // 如果v在sv里面,那么flag变为1
for (int i = 0; i < sv.size(); ++i)
{
if (sv[i] == v)
{
flag = 1;
cout << i << " ";
}
}
if (flag == 0)
{
v = "NIL";
cout << "v is not in sv, v = " << v << endl;
}
return 0;
}
2.1-4 题目:
输入:两个n位以二进制数组形式储存的整数
输出:一个n+1位的以二进制数组形式储存的整数
#include
#include
using namespace std;
vector foo(const vector& v1, const vector& v2)
{
vector ret(v1.size() + 1, 0);
for (int i = v1.size() - 1; i >= 0 ; --i)
{
if (v1[i] + v2[i] == 1)
{
if (ret[i + 1] == 0)
ret[i + 1] = 1;
else // 要么等于0,要么等于1
{
ret[i + 1] = 0;
ret[i] = 1; //进位
}
}
else if (v1[i] + v2[i] == 2)
{
ret[i] = 1;
}
}
return ret;
}
int main()
{
vector iv1 = {1, 1, 1, 0}; // 14
vector iv2 = {1, 0, 0 ,1}; // 9
vector v = foo(iv1, iv2);
cout << "输出应为: 1 0 1 1 1 " << endl << "输 出: ";
for (auto i : v) cout << i << " ";
cout << endl;
return 0;
}
2-2 冒泡排序
冒泡排序基本思想:从左到右遍历数组,将待排序的序列从右到左遍历,如果相邻元素顺序相反,则交换这两个元素,直到没有要交换的元素为止
时间复杂度:O(n^2)
空间复杂度:O(1)
冒泡排序特点:
1. 最好情况为已排序情况,时间复杂度为O(n^2), 优化后为O(n)
2. 最坏情况为反序,复杂度为O(n^2)
3. 优点为思想简单,容易实现
4. 缺点为低效率
#include
using namespace std;
void swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
void bubble_sort(int arr[], int sz)
{
if (sz <= 1) return;
for (int i = 0; i < sz - 1; ++i)
{
for (int j = sz - 1; j > i; --j)
{
if (arr[j] < arr[j - 1])
swap(arr[j], arr[j - 1]);
}
}
}
void bubble_sort_optimize(int arr[], int sz)
{
if (sz <= 1) return;
for (int i = 0; i < sz - 1; ++i)
{
bool flag = false;
for (int j = sz - 1; j > i; --j)
{
if (arr[j] < arr[j - 1])
{
swap(arr[j], arr[j - 1]);
flag = true;
}
}
if (flag == false) return;
}
}
int main()
{
int arr[10] = {5, 8, 9, 1, 0, 3, 7, 2, 4, 6};
cout << "prim: ";
for (int i = 0; i < 10; ++i) cout << arr[i] << " "; cout << endl;
bubble_sort(arr, 10);
cout << "bbst: ";
for (int i = 0; i < 10; ++i) cout << arr[i] << " "; cout << endl;
int arr1[10] = {5, 8, 9, 1, 0, 3, 7, 2, 4, 6};
cout << "prim: ";
for (int i = 0; i < 10; ++i) cout << arr1[i] << " "; cout << endl;
bubble_sort_optimize(arr1, 10);
cout << "prim: ";
for (int i = 0; i < 10; ++i) cout << arr1[i] << " "; cout << endl;
return 0;
}
2.2-2 选择排序
选择排序:n个元素,经过n-1次循环,每次选出待排序序列中最小(大)元素与已排序序列的后一位元素交换
从左到右遍历序列,将最小(大)元素选出来与第一个位置元素交换,次小(大)元素选出来与第二个位置的元素交换,以此类推。。
选择排序时间复杂度:O(n^2)
选择排序空间复杂度:O(1)
选择排序的特点:
1. 优点是思想简单,容易实现
2. 缺点是对于大数据集效率不高
3. 每次循环最多交换一次数据,n个数据完成排序最多交换n-1次数据,相对不错
4. 不是稳定排序
#include
#include
using namespace std;
void swap(int& i, int& j)
{
int tmp = i;
i = j;
j = tmp;
}
void selection_sort(vector& iv)
{
if (iv.size() == 0 || iv.size() == 1) return;
for (int i = 0; i < iv.size(); ++i)
{
int min = iv[i], min_index = i;
for (int j = i + 1; j < iv.size(); ++j)
{
if (iv[j] < min)
{
min = iv[j];
min_index = j;
}
}
if (min_index != i)
swap(iv[min_index], iv[i]);
}
}
int main()
{
vector v = {5, 2, 9, 10, 6, 1, 8, 3, 7, 0, 4};
cout << "原 序 列:";
for (auto i : v) cout << i << " ";
cout << endl;
selection_sort(v);
cout << "排序后序列:";
for (auto i : v) cout << i << " ";
cout << endl;
return 0;
}
2.3-1 题目:说明归并排序在数组A = {3, 41, 52, 26, 38, 57, 9, 49}上的操作
#include
using namespace std;
void merge(int arr[], int low, int mid, int high) // arr[low, mid] and arr[mid + 1, high] are sorted
{
int* tmp = new int[high - low + 1];
int left = low, right = mid + 1, index = 0;
while (left <= mid && right <= high)
{
if (arr[left] <= arr[right]) // <= 保证了稳定排序
{
tmp[index] = arr[left];
++left;
}
else
{
tmp[index] = arr[right];
++right;
}
++index;
}
while (left <= mid)
{
tmp[index] = arr[left];
++left;
++index;
}
while (right <= high)
{
tmp[index] = arr[right];
++right;
++index;
}
for (int i = 0; i < index; ++i, ++low) // 注意边界条件 i < index 和 ++low
arr[low] = tmp[i];
delete [] tmp; // 千万不能忘记
}
void merge_sort(int A[], int low, int high) // A[low, high]
{
if (low < high)
{
int mid = (low + high) / 2;
merge_sort(A, low, mid);
merge_sort(A, mid + 1, high);
merge(A, low, mid, high);
}
}
int main()
{
int A[8] = {3, 41, 52, 26, 38, 57, 9, 49};
cout << "原 序 列:";
for (int i = 0; i < 8; ++i) cout << A[i] << " ";
cout << endl;
merge_sort(A, 0, 7);
cout << "排序后序列:";
for (int i = 0; i < 8; ++i) cout << A[i] << " ";
cout << endl;
return 0;
}
2.3-2 题目:
重写merge,不使用哨兵,而是一旦数组L或R的所有元素均被复制回A就立刻停止,再将另一数组的剩余部分复制回A
void merge(int arr[], int low, int mid, int high) // arr[low, mid] and arr[mid + 1, high] are sorted
{
int* tmp = new int[high - low + 1];
int left = low, right = mid + 1, index = 0;
while (left <= mid && right <= high)
{
if (arr[left] <= arr[right]) // <= 保证了稳定排序
{
tmp[index] = arr[left];
++left;
}
else
{
tmp[index] = arr[right];
++right;
}
++index;
}
while (left <= mid)
{
tmp[index] = arr[left];
++left;
++index;
}
while (right <= high)
{
tmp[index] = arr[right];
++right;
++index;
}
for (int i = 0; i < index; ++i, ++low)
arr[low] = tmp[i];
}
2.3-5 二分查找
#include
using namespace std;
int binary_search(int arr[], int sz, int key)
{
int left = 0, right = sz - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (arr[mid] == key)
return mid;
else if (arr[mid] < key)
left = mid + 1;
else
right = mid - 1;
}
return -1; // 执行到这里就说明没有与key值相等的元素
}
int main()
{
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
cout << "元素1的位置应为1, 输出:" << binary_search(arr, 10, 1) << endl;
cout << "元素5的位置应为5, 输出:" << binary_search(arr, 10, 5) << endl;
cout << "元素11不在arr中,输出应为-1, 输出:" << binary_search(arr, 10, 11) << endl;
return 0;
}
2.3-6 题目:
将插入排序中反向查找已排序序列部分用二分查找改写,将复杂度降为O(nlogn)
#include
using namespace std;
/* 插入排序原版 */
void insertion_sort(int arr[], int sz)
{
if (sz <= 1) return;
for (int i = 1; i < sz; ++i)
{
int key = arr[i];
int j = i - 1;
while (j >= 0 && key < arr[j])
{
arr[j + 1] = arr[j];
--j;
}
arr[j + 1] = key;
}
}
/* 二分优化版本 */
void insertion_sort_optimize(int arr[], int sz)
{
if (sz <= 1) return;
for (int i = 1; i < sz; ++i)
{
int key = arr[i];
int left = 0, right = i - 1; // search range [left, right]
while (left <= right)
{
int mid = (left + right) / 2;
if (arr[mid] <= key)
left = mid + 1; // <= 保证了排序的稳定性,循环结束后,left即为key要插入的位置
else
right = mid - 1;
}
for (int j = i - 1; j >= left; --j) // 将[left, i - 1]的元素copy到[left + 1, i]
arr[j + 1] = arr[j];
arr[left] = key;
}
}
int main()
{
int arr[10] = {5, 1, 0, 9, 4, 2, 6, 6, 3, 8};
cout << "prim: " ;
for (int i = 0; i < 10; ++i) cout << arr[i] << " ";
cout << endl;
insertion_sort(arr, 10);
cout << "sort: ";
for (int i = 0; i < 10; ++i) cout << arr[i] << " ";
cout << endl;
insertion_sort_optimize(arr, 10);
cout << "s_op: ";
for (int i = 0; i < 10; ++i) cout << arr[i] << " ";
cout << endl;
return 0;
}