《C和C++程序员面试秘笈》第9章 排序



Makefile

#---------------------------------------------------------
# 生成可执行文件 execute
PROJECT     = execute
#---------------------------------------------------------
# .o文件
SrcSuf      = c
SrcSuf2     = cpp
SrcSuf3     = cc
ObjSuf      = o
LibSuf      = so
LibSuf2     = a
#---------------------------------------------------------

OBJFILES    =  ./test.$(ObjSuf)       
#---------------------------------------------------------
# 头文件目录
INCLUDEPATH =  -I /usr/include/
INCLUDEPATH += -I /usr/local/include 


# 库目录
LIBPATH     =  -L /usr/local/lib/
LIBPATH     += -L /usr/lib/ 
#---------------------------------------------------------
CC          =   g++

# 编译选项
CFlag       =   $(INCLUDEPATH) -w -g -ggdb -fshort-wchar -std=c++11 -pthread

# 链接选项
LDFLAGS     +=  $(LIBPATH) 
LDFLAGS     +=  -l pthread         
#LDFLAGS     +=  -l hiredis    
#---------------------------------------------------------
.SUFFIXES: .$(SrcSuf) .$(ObjSuf) .$(LibSuf) .$(SrcSuf2) .$(LibSuf2) .$(SrcSuf3)

all:  $(PROJECT) clean
# 生成可执行文件
$(PROJECT):$(OBJFILES)
	@echo "creating $(PROJECT) start..."
	$(CC) $(LDFLAGS) $(OBJFILES) -o $(PROJECT) 
	@echo "creating $(PROJECT) end"
#---------------------------------------------------------
# .c 生成 .o 文件
.$(SrcSuf).$(ObjSuf):
	@echo "Compiling $(PROJECT) $<"
	$(CC) $(CFlag) -c $< -o $@ 
#---------------------------------------------------------
# .cpp 生成 .o 文件
.$(SrcSuf2).$(ObjSuf):
	@echo "Compiling $(PROJECT) $<"
	$(CXX) $(CFlag) -c $< -o $@
#---------------------------------------------------------
# .cc 生成 .o 文件
.$(SrcSuf3).$(ObjSuf):
	@echo "Compiling $(PROJECT) $<"
	$(CXX) $(CFlag) -c $< -o $@
#---------------------------------------------------------
# 删除 .o 文件
clean:
	@echo "Cleaning $(PROJECT) project files"
	@rm -f $(OBJFILES) core

#---------------------------------------------------------
# make cleanall 删除 .o 文件 execute文件
.PHONY:cleanobj
cleanobj:
	-rm -f $(OBJFILES) 
.PHONY:cleanexe
cleanexe:
	-rm -f $(PROJECT) 
.PHONY:cleanall
cleanall:cleanobj cleanexe


1. 插入排序

在这里插入图片描述

#include <iostream>
#include <string>
using namespace std;
/*-------------------- 插入排序 ---------------------- 
	直接插入排序的两个性质:
		- 在最好的情况(序列本身已是有序的)下时间代价为 O(n)
		- 对于短序列,直接插入排序比较有效
	shell 排序有效地利用了直接排序的这两个性质
-----------------------------------------------------*/
//插入排序算法(升序排列)
template <class T>
void ImprovedInsertSort(T Array[], int n)
{
	T TempRecord;				//临时变量
	for (int i = 1; i < n; ++i) //依次插入第 i 个记录
	{
		TempRecord = Array[i];
		int j = i - 1;								//从 i 开始往前寻找记录 i 的正确位置
		while ((j >= 0) && (TempRecord < Array[j])) //将那些大于等于记录 i 的记录后移
		{
			Array[j + 1] = Array[j];
			j = j - 1;
		}
		Array[j + 1] = TempRecord; //此时 j 后面就是记录 i 的正确位置,回填
	}
}
void print_arr(int arr[], int len)
{
	for (int i = 0; i < len; i++)
		cout << arr[i] << ' ';
	cout << endl;
}
int main()
{
	int a1[] = {7, 3, 5, 8, 9, 1, 2, 4, 6};
	int len = sizeof(a1) / sizeof(a1[0]);
	print_arr(a1, len);
	ImprovedInsertSort(a1, len);
	print_arr(a1, len);

	int a2[] = {8, 4, 6, 9, 10, 2, 3, 5, 7};
	len = sizeof(a2) / sizeof(a2[0]);
	print_arr(a2, len);
	ImprovedInsertSort(a2, len);
	print_arr(a2, len);

	getchar();
	return 0;
}



2. 希尔(shell)排序

在这里插入图片描述

#include <iostream>
#include <string>
using namespace std;
/*--------------- shell 排序 --------------------- 
	最佳情况时间是 O(N × 1) = O(N), 
	最坏情况时间是 O(N × N) = O(N²). 

	希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;
	随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰
	被分成一组,算法便终止。

	稳定性: 希尔排序是非稳定排序算法。

-------------------------------------------------*/
void shellSort1(int array[], int len)
{
	int gap = len; // 初始步长
	do
	{
		gap = gap / 3 + 1;			  // 步长递减公式--O(n*1.3)
		for (int i = 0; i < gap; ++i) // 分组,对每一组,进行插入排序
		{
			for (int j = i + gap; j < len; j += gap) // 插入排序
			{
				int tmp = array[j]; // 临时变量
				int index = j - gap;
				while ((index >= 0) && (tmp < array[index])) // 从后往前遍历
				{
					array[index + gap] = array[index]; // 后移
					index -= gap;
				}
				array[index + gap] = tmp; // 回填
			}
		}
	} while (gap > 1);
}
void print_arr(int arr[], int len)
{
	for (int i = 0; i < len; i++)
		cout << arr[i] << ' ';
	cout << endl;
}
int main()
{
	int a1[] = {7, 3, 5, 8, 9, 1, 2, 4, 6};
	int len = sizeof(a1) / sizeof(a1[0]);
	print_arr(a1, len);
	shellSort1(a1, len);
	print_arr(a1, len);

	int a2[] = {8, 4, 6, 9, 10, 2, 3, 5, 7};
	len = sizeof(a2) / sizeof(a2[0]);
	print_arr(a2, len);
	shellSort1(a2, len);
	print_arr(a2, len);

	getchar();
	return 0;
}



3. 冒泡排序

在这里插入图片描述

#include <iostream>
#include <string>
using namespace std;
/********************* 冒泡排序 *********************
	冒泡排序算法的运作如下:(从后往前)
	1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
	2. 每次外层循环:对每一对相邻元素作同样的工作
	3. 针对所有的元素重复以上的步骤,除了最后一个。
	4. 持续每次对越来越少的元素重复上面的步骤,
	   直到没有任何一对数字需要比较。
	O(n²) 
	稳定性:冒泡排序是一种稳定排序算法
***************************************************/
//冒泡排序(升序)
void bubbleSort1(int *array, int len) //array[len]
{
	for (int i = 0; i < len - 1; ++i) //进行len-1趟扫描
	{
		for (int j = 0; j < len - 1 - i; ++j) //第一趟从([0]与[0+1])...比较到([len-2-i]与[len-2-i+1]);然后是第二趟
		{
			if (array[j] > array[j + 1])
			{
				int tmp = array[j];
				array[j] = array[j + 1];
				array[j + 1] = tmp;
			}
		}
	}
}

// 冒泡排序的优化
// 添加标志位flag:false-表示没有排好, true-表示已经排好
void bubbleSort2(int *array, int len)
{
	int flag = false;
	for (int i = 0; (i < len - 1) && (flag == false); ++i)
	{
		flag = true; // 设置标志
		for (int j = 0; j < len - 1 - i; ++j)
		{
			if (array[j] > array[j + 1])
			{
				int tmp = array[j];
				array[j] = array[j + 1];
				array[j + 1] = tmp;
				flag = false; //没有排好
			}
		}
	}
}
void print_arr(int arr[], int len)
{
	for (int i = 0; i < len; i++)
		cout << arr[i] << ' ';
	cout << endl;
}
int main()
{
	int a1[] = {7, 3, 5, 8, 9, 1, 2, 4, 6};
	int len = sizeof(a1) / sizeof(a1[0]);
	print_arr(a1, len);
	bubbleSort1(a1, len);
	print_arr(a1, len);

	int a2[] = {8, 4, 6, 9, 10, 2, 3, 5, 7};
	len = sizeof(a2) / sizeof(a2[0]);
	print_arr(a2, len);
	bubbleSort1(a2, len);
	print_arr(a2, len);

	getchar();
	return 0;
}



4. 快速排序

在这里插入图片描述

#include <iostream>
#include <string>
using namespace std;
/*--------------- 快速排序 --------------------- 
	数组排序任务可以如下完成:
	1)设 k=a[0], 将 k 挪到适当位置,使得比 k 小的元素都
	在 k 左边,比 k 大的元素都在 k 右边,和 k 相等的,不关心
	在 k 左右出现均可(O(n)时间完成)
	2) 把 k 左边的部分快速排序
	3) 把 k 右边的部分快速排序

	核心:挖坑填数
-------------------------------------------------*/
void swap(int &a, int &b) //交换变量 a,b 值
{
	int tmp = a;
	a = b;
	b = tmp;
}
/****
 *	参数
 *		a 数组
 *		start 数组的第一个有效下标
 *		end   数组的最后一个有效下标
 */		
void QuickSort(int a[], int start, int end)
{
	if (start >= end)
		return;
	int k = a[start];//每次的基准数:可以随意设置,我习惯使用数组首元素
	int i = start, j = end;
	while (i != j)
	{
		while (i < j && a[j] >= k) //从右往左找比 k 小的
			--j;
		swap(a[i], a[j]);
		while (i < j && a[i] <= k) //从左往右找比 k 大的
			++i;
		swap(a[i], a[j]);
	}//此时k放在了合适的位置(i==j,a[i] == k,并且左边元素都比k小,右边的元素都比k大)
	QuickSort(a, start, i - 1); //把 k 左边的部分快速排序
	QuickSort(a, i + 1, end);	//把 k 右边的部分快速排序
}
void print_arr(int arr[], int len)
{
	for (int i = 0; i < len; i++)
		cout << arr[i] << ' ';
	cout << endl;
}
int main()
{
	int a1[] = {7, 3, 5, 8, 9, 1, 2, 4, 6};
	int len = sizeof(a1) / sizeof(a1[0]);
	print_arr(a1, len);
	QuickSort(a1, 0, len - 1);
	print_arr(a1, len);

	int a2[] = {8, 4, 6, 9, 10, 2, 3, 5, 7};
	len = sizeof(a2) / sizeof(a2[0]);
	print_arr(a2, len);
	QuickSort(a2, 0, len - 1);
	print_arr(a2, len);

	getchar();
	return 0;
}

top10问题

有很多数字 a[500],要求打印前10小的数

	快排:结束条件是,基准索引停下的位置+待排序序列开始的位置 == 10
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

//************************快速排序**************************//
/****
 *	参数
 *		a 数组
 *		start 数组的第一个有效下标
 *		end   数组的最后一个有效下标的 下一位
 */
void quick_sort(int *a, int start, int end)
{
	if (end - start <= 1)
	{
		return;
	}
	int head = start;
	int tail = end - 1;
	int pivot = (head + tail) / 2;

	while (tail > head)
	{
		while (head < pivot && a[head] <= a[pivot])
			head++;

		int tmp = a[head];
		a[head] = a[pivot];
		a[pivot] = tmp;
		pivot = head;

		while (tail > pivot && a[tail] >= a[pivot])
			tail--;
		tmp = a[tail];
		a[tail] = a[pivot];
		a[pivot] = tmp;
		pivot = tail;
	}

	quick_sort(a, start, pivot);
	quick_sort(a, pivot + 1, end);
}
//************************快速排序解决Top10问题**************************//
void top10(int *a, int start, int end)
{
	if (end - start <= 1 || start > 9)
	{
		return;
	}

	int head = start;
	int tail = end - 1;
	int pivot = (head + tail) / 2; //基准值下标
	while (tail > head)
	{
		while (head < pivot && a[head] <= a[pivot])
			head++;

		int tmp = a[head];
		a[head] = a[pivot];
		a[pivot] = tmp;
		pivot = head;

		while (tail > pivot && a[tail] >= a[pivot])
			tail--;
		tmp = a[tail];
		a[tail] = a[pivot];
		a[pivot] = tmp;
		pivot = tail;
	}

	if (start + pivot == 9) //找到
	{
		return;
	}
	if (start + pivot > 9) //对左侧继续
	{
		top10(a, start, pivot);
	}
	if (start + pivot < 9) //对右侧继续
	{
		top10(a, pivot + 1, end);
	}
}

int main()
{
	//-----------------------快速排序测试--------------------------//
	puts("------------------------快速排序测试-------------------------");
	int a[10] = {5, 6, 9, 3, 2, 1, 4, 7, 7, 1};
	quick_sort(a, 0, 10);
	for (int i = 0; i < 10; i++)
	{
		printf("%d\t", a[i]);
	}
	puts("\n\n");

	//------------------------Top10测试-------------------------//
	puts("------------------------快速排序的Top10测试-------------------------");
	srand(time(NULL));
	int a2[100];
	for (int i = 0; i < 100; i++)
	{
		a2[i] = rand() % 200;
		printf("%d  ", a2[i]);
	}
	puts("\n--------------");
	top10(a2, 0, 100);
	for (int i = 0; i < 10; i++)
	{
		printf("%d\t", a2[i]);
	}
	puts("\n\n");
}


5. 选择排序

在这里插入图片描述

#include <iostream>
#include <string>
using namespace std;
/*-------------------- 选择排序 -------------------- 

	每一次外层循环,从待排序的序列中选出最小的
	一个元素,存放在本次待排序序列的起始位置
	..... 
	
	找到第 0 小,放在第 0 个位置上
	找到第 1 小,放在第 1 个位置上
	..... 

	稳定性:选择排序是不稳定的排序方法 如:[5,5,3]
-------------------------------------------------*/
//选择排序(升序排列:从小到大)
void selectionSort(int *array, int len)
{
	int min = 0;					  //指向最小的元素的位置
	for (int i = 0; i < len - 1; ++i) //外层循环
	{
		min = i;
		for (int j = i + 1; j < len; ++j) //内层循环
		{
			if (array[min] > array[j]) //判断
			{
				min = j; // 保存最小的元素的位置
			}
		}
		if (min != i) // 判断是否需要交换
		{			  // 找到了新的最小值--交换
			int tmp = array[min];
			array[min] = array[i];
			array[i] = tmp;
		}
	}
}
void print_arr(int arr[], int len)
{
	for (int i = 0; i < len; i++)
		cout << arr[i] << ' ';
	cout << endl;
}
int main()
{
	int a1[] = {7, 3, 5, 8, 9, 1, 2, 4, 6};
	int len = sizeof(a1) / sizeof(a1[0]);
	print_arr(a1, len);
	selectionSort(a1, len);
	print_arr(a1, len);

	int a2[] = {8, 4, 6, 9, 10, 2, 3, 5, 7};
	len = sizeof(a2) / sizeof(a2[0]);
	print_arr(a2, len);
	selectionSort(a2, len);
	print_arr(a2, len);

	getchar();
	return 0;
}



6. 堆排序



7. 归并排序(自顶向下)

递归分解,返回合并

合并:两个序列同时遍历,选择小的追加到新序列,然后移动游标...

在这里插入图片描述

#include <iostream>
#include <vector>
#include <stdlib.h>
using namespace std;

// this is a sort function
void merge_sort(vector<int> &li)
{
	int len = li.size();
	if (len <= 1) //分到只含有一个元素
	{
		return;
	}

	vector<int> left(li.begin(), li.begin() + len / 2); //左半段
	vector<int> right(li.begin() + len / 2, li.end());	//右半段

	merge_sort(left);  //左半段 已经有序
	merge_sort(right); //右半段 已经有序

	li.clear();

	//合并左右 到 vector<int> &li 中
	int lindex = 0;
	int rindex = 0;
	while (lindex < left.size() && rindex < right.size())
	{
		if (left[lindex] <= right[rindex])
		{
			li.push_back(left[lindex]);
			lindex++;
		}
		else
		{
			li.push_back(right[rindex]);
			rindex++;
		}
	}

	li.insert(li.end(), left.begin() + lindex, left.end());
	li.insert(li.end(), right.begin() + rindex, right.end());
	//此时li是合并后的有序的...
}

int main()
{
	srand(time(NULL));

	vector<int> li;
	for (int i = 0; i < 16; i++)
	{
		int a = rand() % 50;
		li.push_back(a);
		cout << a << " ";
	}

	cout << endl;

	merge_sort(li);
	for (auto itr : li)
	{
		cout << itr << " ";
	}
	cout << endl;
}



8. 基数排序



9. 排序算法的性能比较



10. 排序算法的时间复杂度比较



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值