学习笔记——《算法导论》第2章习题

前言:这个月开始拜读《算法导论》,难度挺大的,在这里记录一下课后习题的思路和代码实现。

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;

}



你可能感兴趣的:(MSP)