《算法导论》第三版第2章 算法基础 解读

1.1插入排序  

插入排序(Insertion Sort)是一种简单直观的排序算法,其工作原理类似于我们按数字顺序排列扑

克牌。在插入排序中,数组被虚拟分为“已排序”和“未排序”两个部分。排序过程中,逐个将“未排序”

的元素插入到“已排序”部分的适当位置,直到所有元素都被排序。具体步骤如下:

初始化:将数组的第一个元素视为已排序部分,其余部分视为未排序部分。

迭代过程:

  1. 从未排序部分取出第一个元素(标记为 key)。将 key 与已排序部分的元素从后向前逐一比较。

  2. 如果已排序部分的当前元素比 key 大,则将该元素向后移动一位。
  3. 重复这个比较和移动的过程,直到找到 key 应该插入的位置。
  4. key 插入到这个位置。

重复:重复上述迭代过程,直到未排序部分的所有元素都被处理完毕。

插入排序的主要特点是简单和高效,特别是对于小型数组。在最佳情况下(即数组已经是排序状态),插入排序的时间复杂度为 (O(n)),但在平均情况和最坏情况下(即数组是随机排列或逆序排列),其时间复杂度为 (O(n^2))。因此,对于大型数组,插入排序可能不是最佳选择

以下是插入排序的一个简单示例:

代码

#include<stdio.h>
//插入排序
int main()
{
	int arr[] = {5,3,1,4,6};
	int key,i,j;
	//排序前
	printf("排序前:");
	for (i = 0; i < 5; i++)
		printf("%d ", arr[i]);
	printf("\n");

	for (i = 1; i < 5; i++)
	{
		key = arr[i];//取出未排序部分的第一个元素
		j = i - 1;

		//将已排序部分的元素与key比较并向后移动
		while (j >= 0 && arr[j] > key)
		{
			arr[j + 1] = arr[j];
			j--;
		}
		arr[j + 1] = key;//插入key到正确位置
		

	}
	//打印结果
	printf("排序后:");
	for (i = 0; i < 5; i++)
		printf("%d ", arr[i]);

	return 0;
}

1.2伪代码中的一些约定

1.  for j=2 to A.length
2.      key = A[j]
3.      //Insert A[j] into sorted sequence A[1..j-1].
4.      i = j - 1
5.      while i > 0 and A[i] > key
6.          A[i + 1] = A[i]
7.          i = i - 1  
8.      A[i + 1] = key

1.缩进表示块结构

       例如:第一个块结构就是由for循环开始的2--8行;第二个块结构就是由while循环开始的6---7                      行

2.形如i = j = e的多重赋值将表达式 e 的值赋给变量 i 和 j

     其实就是   j = e;                                                                                                                                                i = j;

3.数组元素通过“ 数组名[下标] ”这样的形式来访问

       例如:A[ i ]表示数组A的第 i 个元素

4.复合数据通常被组织成对象,对象又由属性组成

       例如:A.length就表示数组A中的元素数目

5.伪代码中允许在单一的 return 语句中返回多个值

6.布尔运算符 ”and“ 和 ”or“ 都是短路的

      and相当于&&;or相当于 || ;与c语言的用法一致

7.关键词 error 表示因为已被调用的过程情况不对而出现了一个错误

Function Divide(x, y)
    If y == 0 Then
        Error "除数不能为零"
    Else
        Return x / y
    EndIf
EndFunction

8.其余同c语言用法

1.3分析算法

分析算法(Algorithm analysis)是计算机科学中的一个重要领域,它涉及评估算法的效率和效果。分析的目的是预测算法对于不同规模的输入数据所需的计算资源,包括时间和空间资源。通过分析算法,我们可以确定哪一种算法最适合解决特定的问题,以及在特定的计算环境和约束条件下,哪一种算法表现得最好。以下是分析算法时考察的几个关键指标:

  1. 时间复杂度:算法的时间复杂度描述了算法执行所需时间随输入数据规模增加的增长速率。常用大O符号(Big O notation)来表示,如O(1), O(log n), O(n), O(n^2)等。

  2. 空间复杂度:算法的空间复杂度描述了算法执行过程中所需内存空间的增长速率。它也用大O符号来表示。

  3. 最好、最坏和平均情况:算法的表现可能因输入数据的不同而不同。分析算法时,我们通常考虑最好情况(Best Case)、平均情况(Average Case)和最坏情况(Worst Case)下的时间和空间复杂度。

  4. 渐进效率:分析算法时,我们通常关注大规模输入下的表现,即算法的渐进效率,因为小规模输入下的性能差异通常不够显著。

  5. 实际性能:理论分析提供了算法性能的一个上界估计,但实际性能还会受到编程语言、硬件特性、编译器优化等多种因素的影响。

  6. 算法的正确性和鲁棒性:除了效率之外,算法分析还要确保算法能够正确解决问题,并且能够处理错误输入或异常情况。

1.4设计算法:分治法

分治法(Divide and Conquer)是一种重要的算法设计策略,它将复杂的问题分解成若干个规模较小的相同或相似的子问题,递归地解决这些子问题,然后再将这些子问题的解合并以解决原来的问题。分治法经常用于解决复杂性较高的问题,因为它可以简化问题的难度,使问题变得更加可管理。

分治法的步骤通常包括三个主要部分:

分解(Divide):将原问题分解成若干个规模更小的子问题,这些子问题相互独立(或几乎相互独立),并与原问题形式相同。

解决(Conquer):递归地解决这些子问题。如果子问题的规模足够小,则直接解决;否则,继续递归分解。

合并(Combine):将子问题的解合并成原问题的解。

1.4.1归并排序

归并排序算法完全遵循分治模式。直观上其操作如下:

分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列。

解决:使用归并排序递归地排列两个子序列。

合并:合并两个已排序的子序列以产生已排序的答案。

下面是根据《算法导论》上面的伪代码编写的c语言程序

#include <stdio.h>
#include <stdlib.h>

//排序
void split(int arr[], int s, int e, int middle)
{
	int i, j, k;
	int n1 = middle - s + 1;
	int n2 = e - middle;

	int* L = (int*)malloc(n1 * sizeof(int));
	int* R = (int*)malloc(n2 * sizeof(int));

	//检查内存分配是否成功
	if (L == NULL || R == NULL) {
		fprintf(stderr, "Memory allocation failed\n");
		free(L);
		free(R);
		return;
	}


	for (i = 0; i < n1; i++)
		L[i] = arr[i+s];
	for (j = 0; j < n2; j++)
		R[j] = arr[middle + 1 + j];
	i = 0; 
	j = 0;
	k = s; 

	while (i < n1 && j < n2) {
		if (L[i] <= R[j]) {
			arr[k] = L[i];
			i++;
		}
		else {
			arr[k] = R[j];
			j++;
		}
		k++;
	}

	
	while (i < n1) {
		arr[k] = L[i];
		i++;
		k++;
	}

	
	while (j < n2) {
		arr[k] = R[j];
		j++;
		k++;
	}


	free(L);
	free(R);
}


//拆分并链接排序函数split
void split_sort(int arr[], int s, int e)
{
	if (s < e)
	{
		int middle = (e - s) / 2 + s;

		split_sort(arr, s, middle);
		split_sort(arr, middle + 1, e);

		split(arr, s, e, middle);
	}
}



//打印函数
void printf_arr(int arr[],int arr_size)
{
	for (int i = 0; i < arr_size; i++)
		printf("%d ", arr[i]);
}

//主函数
int main()
{
	int arr[] = { 12, 11, 13, 5, 6, 7 };
	int arr_size = sizeof(arr) / sizeof(arr[0]);
    
	//排序前
	printf("start:");
	printf_arr(arr, arr_size);

	//排序后
	split_sort(arr, 0, arr_size-1);
	printf("\nend:");
	printf_arr(arr, arr_size);


	return 0;
}

  • 26
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你的告白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值