【2023.3.5】MOOC程序设计与算法
创始人
2024-06-03 07:31:39
0

【2023.3.5】MOOC程序设计与算法笔记

文章目录

  • 【2023.3.5】MOOC程序设计与算法笔记
    • 说明
    • 一、枚举
    • 二、递归
      • 1-汉诺塔问题
      • 2-N皇后问题
      • 3-逆波兰表达式
      • 4、全排列问题
    • 三、二分算法
      • 1-BinarySearch函数
    • 四、分治
      • 1-归并排序
      • 2-快速排序
    • 五、深度优先搜索
      • 1-基本模型-通用套路
      • 2-迷宫问题
        • (1)基于**递归**的写法
        • (2)基于**堆栈**的写法
    • 六、广度优先搜索BFS
      • 0-quene队列的使用
      • 1-洛谷P1162 填涂颜色
      • 2-洛谷P1443 马的遍历

说明

学习视频:MOOC:程序设计与算法(二)算法基础

做的一些笔记。

一、枚举

二、递归

1-汉诺塔问题

#includeint k=0;
//汉诺塔问题,n代表原始盘子数,原始盘为src,mid作为中转盘,dest作为目标盘
void Hanoi(int n, char src, char mid, char dest) {if (n == 1)printf("%d: %c->%c\n",++k, src, dest);else {Hanoi(n - 1, src, dest, mid);printf("%d: %c->%c\n",++k, src, dest);Hanoi(n - 1, mid, src, dest);}}
int main() {int n;printf("input n:");scanf("%d", &n);Hanoi(n, 'A', 'B', 'C');return 0;
}

2-N皇后问题

N皇后问题是一个经典的问题,在一个N*N的棋盘上放置N个皇后,每行一个并使其不能互相攻击(同一行、同一列、同一斜线上的皇后都会自动攻击)。

#include
#includeint queenPos[50] = { 0 };
int N;//N个皇后
int num = 0;//num种摆法。//在0~k-1个皇后已经摆好的情况下,摆放第k个及以后的皇后
void Nqueen(int k);void printQueenPos(int* queenPos, int k);int main() {printf("input n:");scanf("%d", &N);Nqueen(0);return 0;
}void printQueenPos(int* queenPos, int k) {printf("---------- %d  ---\n",++num);for (int i = 0; i < k; i++) {//打印步骤printf("%d ", queenPos[i]);}putchar('\n');putchar('\n');//制图for (int i = 0; i < k; i++) {for (int j = 0; j < k; j++) {if (j  == queenPos[i])putchar('*');elseputchar('O');putchar(' ');}putchar('\n');}putchar('\n');
}//在0~k-1个皇后已经摆好的情况下,摆放第k个及以后的皇后
void Nqueen(int k) {int i;if (k == N) {printQueenPos(queenPos, k);}//逐步尝试第k个皇后的位置,摆在k行第i列for (i = 0; i < N; i++) {//遍历已经拜访的位置,看是否合理int j;for (j = 0; j < k; j++) {//两皇后成对角线关联,则他们的 行差的绝对值 == 列差的绝对值if (i == queenPos[j] || abs(j - k) == abs(i - queenPos[j])) {//此时表示该摆法不合理break;}}if (j == k) {//此时表示 该摆法合理queenPos[k] = i;Nqueen(k + 1);}}}

3-逆波兰表达式

定义:

image-20230303101755748

测试样例:

代码:

#include
#include//读入一个逆波兰表达式,并计算其值。
double exp() {char s[20];scanf("%s", s);switch (s[0]){case '+':return exp() + exp();case '-':return exp() - exp();case '*':return exp() * exp();case '/':return exp() / exp();default:return atof(s);}
}int main() {int n;printf("input:");printf("%lf\n", exp());return 0;
}

4、全排列问题

#include
#define Max 20int ans[Max] = { 0 };//记录答案
int n;//表示n的全排列
int Visit[Max] = { 0 };//记录是否排列,Visit[k]表示k已排列
int k = 0;//记录全排列的数量void dfs(int step);//step表示dfs递归的层数,即深度优先搜索的步数
void PrintAns(int* ans, int n);//打印答案int main()
{printf("Please input n:");scanf("%d", &n);dfs(1);printf("Total K:%d", k);return 0;
}void dfs(int step) {if (step > n) {PrintAns(ans , n);k++;return;}for (int i = 1; i <= n; i++) {//表示没有排过序if (!Visit[i]) {Visit[i] = 1;ans[step - 1] = i;dfs(step + 1);Visit[i] = 0;ans[step - 1] = 0;}}}void PrintAns(int* ans, int n) {for(int i=0;i

三、二分算法

1-BinarySearch函数

#include
//在包含size个元素的、从小到大排序的int数组a里查早元素p。
//如果找到,则返回元素下标。否则返回-1。复杂度O(log(n))
int BinarySeach(int a[], int size, int p) {int L = 0;int R = size - 1;while (L <= R) {int mid = L + (R - L) / 2;if (a[mid] == p) {return mid;}if (a[mid] > p)R = mid - 1;elseL = mid + 1;}return -1;
}int main() {int a[] = { 0,1,3,4,6,7,9,11,15,18 };int n;printf("input:");scanf("%d", &n);int i = BinarySeach(a, 10, n);printf("a[%d]=%d\n",i, a[i] );return 0;
}

四、分治

把一个任务分成形式与原任务相同、规模更小的多个任务,分别完成。

1-归并排序

一般用递归实现。

菜鸟教程:归并排序:含动图解释。

#include
#includevoid MergeSort(int* a, int n);
void Merge(int* a,  int mid, int end);int main() {int a[100] = { 2,4,5,8,1,14,9,12,15,33,3 ,44,22,11,33};int len = 0;for (len = 0; a[len]; len++);printf("%d\n", len);MergeSort(a, len);for (int i = 0; i < len; i++)printf("%d ", a[i]);
}void MergeSort(int* a, int n) {if (n <= 1) {return;}MergeSort(a, n / 2);MergeSort(a + n / 2, n - n / 2);Merge(a,  n / 2, n  );}
void Merge(int* a,  int mid, int end) {int* b = (int* )malloc(end*sizeof(int));int i = 0, j = mid;int k = 0;while (i < mid && j < end) {if (a[i] < a[j]) {b[k++] = a[i++];}else {b[k++] = a[j++];}}while (i < mid) {b[k++] = a[i++];}while (j < end) {b[k++] = a[j++];}for (i = 0; i < k; i++)a[i] = b[i];free(b);
}

2-快速排序

image-20230303211234661

i 和 j 同时往中间移动。

image-20230303211247746

快速排序

#include
#includevoid QuickSort(int* a, int n);
void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}int main() {int a[100] = { 22,11,33,2,4,5,8,1,14,9,12,15,55,3 ,44};int len = 0;for (len = 0; a[len]; len++);printf("%d\n", len);QuickSort(a, len);for (int i = 0; i < len; i++)printf("%d ", a[i]);
}void QuickSort(int* a, int n) {if (n <= 1)return;int k = a[0];int i = 0, j = n - 1;while (i != j) {while (i < j) {if (a[j] >= k)j--;else {swap(a + i, a + j);break;}}while (i < j) {if (a[i] <= k) {i++;}else {swap(a + i, a + j);break;}}		}//printf("i=%d,j=%d\n", i, j);//重点:此时再将i左边和右边分开快速排序,所以要减一加一,避开第i个。QuickSort(a , i);QuickSort(a + i + 1, n - i-1);
}

五、深度优先搜索

1-基本模型-通用套路

深度优先搜索dfs基本模型-通用套路:摘自:深度优先搜索DFS-C语言实现、思路/解析-简笔

void dfs(step)
{/*1.判断边界,判断本阶段的DFS是否已经结束了*/if(foo == bar){/*Do something*/return ;}/*2.尝试每一种可能,类似于走迷宫中的在每一个点尝试每个方向*/for(i=0;i<_MAX_;++i){do_something();dfs(step+1);//进行下一步undo_something();}return ;
}

2-迷宫问题

已知迷宫地图,输出迷宫的路程解和步数。

//10*10迷宫地图
//S表示起点,E表示出口,#表示墙,O表示路
//该地图有两个解
char map[N + 1][N + 1] = {{"SO##OOO###"},{"#OOOO#OOO#"},{"#O##O###O#"},{"OOO#OO##O#"},{"O#O##OO###"},{"O#####O###"},{"O#OOOOO###"},{"OOO#O#####"},{"###OOO#OOE"},{"##OO#OOO##"},
}; 

通过dfs跑出迷宫答案

dfs,深度优先搜索,就是从一个节点一直往深处搜索,直到走到尽头,再往回走,走下一个节点,再次深搜……就相当于来回遍历,一个人走迷宫。

是基于回溯的思想。而回溯就是基于栈结构。

所以有两种写法,一种是直接递归,一种是自己构造堆栈、压栈出栈。

(1)基于递归的写法

不过递归调用,本就是由系统的堆栈支持的。所以也算堆栈结构。

不用自己实现堆栈,实现起来肯定要简单些。

#include
#define Max 50
#define MapN 10//表示N*N迷宫/*该地图唯一解 
//10*10迷宫地图,S表示起点,E表示出口,#表示墙,O表示路
char map[N+1][N+1] = {{"SO##OOO###"},{"#OOOO#OOO#"},{"#O##O###O#"},{"#OO#OO##O#"},{"O#O##OO###"},{"O#####O###"},{"O#OOOOO###"},{"OOO#O#####"},{"###OOO#OOE"},{"##OO#OOO##"},
};*///10*10迷宫地图,S表示起点,E表示出口,#表示墙,O表示路
//该地图有两个解
char map[MapN + 1][MapN + 1] = {{"SO##OOO###"},{"#OOOO#OOO#"},{"#O##O###O#"},{"OOO#OO##O#"},{"O#O##OO###"},{"O#####O###"},{"O#OOOOO###"},{"OOO#O#####"},{"###OOO#OOE"},{"##OO#OOO##"},
}; int Visit[Max][Max] = { 0 };void dfs(int x, int y, int step);
void PrintAns(int Ans[Max][Max], int step);int main() {int startX = 0, startY = 0, step = 0;Visit[startX][startY] = 1;dfs(startX, startY, step);return 0;
}void dfs(int x, int y, int step) {if (map[x][y] == 'E') {PrintAns(Visit, step);return;}for (int i = 0; i < 4; i++) {int NextX = x, NextY = y;switch (i){case 0:NextX += 1; break;case 1:NextX -= 1; break;case 2:NextY += 1; break;case 3:NextY -= 1; break;}if (NextX >= 0 && NextX < MapN && NextY >= 0 && NextY < MapN) {if (map[NextX][NextY] != '#' && Visit[NextX][NextY] != 1) {Visit[NextX][NextY] = 1;dfs(NextX, NextY, step + 1);Visit[NextX][NextY] = 0;}}}}void PrintAns(int Ans[Max][Max], int step) {for (int i = 0; i < MapN; i++) {for (int j = 0; j < MapN; j++) {if (Ans[i][j]) {putchar('*');}else {putchar(' ');}}putchar('\n');}printf("Total Steps:%d\n", step);putchar('\n');}

(2)基于堆栈的写法

基于堆栈结构的写法,(我不大熟悉,尝试着写,写得不好)

后面才知道,c++有现成的stack容器,用于使用堆栈这种数据结构,不用我们自己写压栈、出栈这些函数,

使用教程参考:C语言-stack的应用

#include
#include
#define Max 50
#define MapN 10//表示N*N迷宫//10*10迷宫地图,S表示起点,E表示出口,#表示墙,O表示路
//该地图有两个解
char map[MapN + 1][MapN + 1] = {{"SO##OOO###"},{"#OOOO#OOO#"},{"#O##O###O#"},{"OOO#OO##O#"},{"O#O##OO###"},{"O#####O###"},{"O#OOOOO###"},{"OOO#O#####"},{"###OOO#OOE"},{"##OO#OOO##"},
}; struct StepInStatk {int x;int y;int k;//记录已走过的上下左右,0~3,4表示所有方向已走过int step;//第多少步。StepInStatk* pNext;
};int Visit[Max][Max] = { 0 };//记录改坐标是否已走过。void dfsByStack(StepInStatk* pFirstStep,int step);
void PrintAns(StepInStatk* pStackTop);
StepInStatk* InitStep(int x,int y);//初始化迷宫的起始位置
StepInStatk* PushStepStack(StepInStatk* pStackTop, int x,int y);//压栈
StepInStatk* PopStepStack(StepInStatk* pStackTop);//出栈int main() {int startX = 0, startY = 0;StepInStatk* pFirstStep = InitStep(startX, startY);Visit[startX][startY] = 1;dfsByStack(pFirstStep, 0);return 0;
}void dfsByStack(StepInStatk* pFirstStep, int step) {StepInStatk* pStackTop = pFirstStep;while (1) {int i;for (i = pStackTop->k; i < 4; i++) {int NextX = pStackTop->x;int NextY = pStackTop->y;switch (i){case 0:NextX += 1; break;case 1:NextX -= 1; break;case 2:NextY += 1; break;case 3:NextY -= 1; break;}if (NextX >= 0 && NextX < MapN && NextY >= 0 && NextY < MapN) {if (Visit[NextX][NextY] == 0 && map[NextX][NextY] != '#') {Visit[NextX][NextY] = 1;//标记这个位置已走过。pStackTop->k = i + 1;//迷宫的走法上下左右,k表示下一次应走的方向,k=4时表示4个方向皆走过了。pStackTop = PushStepStack(pStackTop, NextX, NextY);break;}}}if (map[pStackTop->x][pStackTop->y] == 'E') {//找到出口PrintAns(pStackTop);Visit[pStackTop->x][pStackTop->y] = 0;pStackTop = PopStepStack(pStackTop);}else if (i == 4) {//表示该节点的四个方向都已经走过了,已无路可走,需要回溯。Visit[pStackTop->x][pStackTop->y] = 0;pStackTop = PopStepStack(pStackTop);}if (pStackTop == NULL)//只有当栈底,也就是第一个起始节点的四个方向都遍历完,才会pop出栈,使pStackTop指向NULL。break;}
}StepInStatk* InitStep(int x, int y) {StepInStatk* pFirstStep = (StepInStatk*)malloc(sizeof(StepInStatk));pFirstStep->x = x;pFirstStep->y = y;pFirstStep->k = 0;pFirstStep->step = 0;pFirstStep->pNext = NULL;return pFirstStep;
}StepInStatk* PushStepStack(StepInStatk* pStackTop, int x, int y) {StepInStatk* pStep = (StepInStatk*)malloc(sizeof(StepInStatk));pStep->x = x;pStep->y = y;pStep->k = 0;pStep->step = pStackTop->step + 1;pStep->pNext = pStackTop;return pStep;
}
StepInStatk* PopStepStack(StepInStatk* pStackTop) {return pStackTop->pNext;
}void PrintAns(StepInStatk* pStackTop) {StepInStatk* pStep = pStackTop;int Ans[MapN+1][MapN+1] = { 0 };int step = pStackTop->step;while (pStep != NULL) {Ans[pStep->x][pStep->y] = 1;pStep = pStep->pNext;}for (int i = 0; i < MapN; i++) {for (int j = 0; j < MapN; j++) {if (Ans[i][j]) {putchar('*');}else {putchar(' ');}}putchar('\n');}printf("Total Steps:%d\n", step);putchar('\n');}

六、广度优先搜索BFS

广度优先算法,基于队列的数据结构。

学习视频:B站:洛谷 普及组试炼场 - 广度优先搜索 (BFS)

0-quene队列的使用

使用队列数据结构,c++里有现成的容器queue。

queue的使用案例:C语言-队列(queue)的应用

#include 
#include 
using namespace std;int main(){queue q;for(int i = 0; i < 5; i++){q.push(i);cout << "成功将" << i << "入队" << endl;}cout << endl;while(!q.empty()){cout << "当前队首:" << q.front() << "\t当前队尾:" << q.back();cout << "\t当前队列大小:" << q.size() << endl;q.pop();}return 0;
}

1-洛谷P1162 填涂颜色

洛谷:P1162 填涂颜色

#include 
#include 
using namespace std;struct location {int x;int y;
};
int xx[] = { 1,0,-1,0 };
int yy[] = { 0,1,0,-1 };
int aa[40][40] = { 0 };
int visit[40][40] = { 0 };
int main() {int n = 0;cin >> n;for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {cin >> aa[i][j];}}queue queue;location Temp = { 0,0 };queue.push(Temp);visit[0][0] = 1;while (!queue.empty()) {for (int i = 0; i < 4; i++) {int dx = queue.front().x + xx[i];int dy = queue.front().y + yy[i];if (dx >= 0 && dx <= n + 1 && dy >= 0 && dy <= n + 1) {if (visit[dx][dy] == 0 && aa[dx][dy] == 0) {visit[dx][dy] = 1;Temp.x = dx;Temp.y = dy;queue.push(Temp);}}}queue.pop();}for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (visit[i][j] == 1 || aa[i][j] == 1) {cout << aa[i][j] << " ";}else {cout << "2 ";}}cout << endl;}return 0;
}

2-洛谷P1443 马的遍历

洛谷:P1443 马的遍历

#include 
#include 
using namespace std;struct location {int x;int y;int s;//表示步数,第几步
};
int xx[] = { 1, 1, 2, 2,-1,-1,-2,-2};
int yy[] = { 2,-2, 1,-1, 2,-2, 1,-1};
int map[402][402] = { -1 };
int visit[402][402] = { 0 };
int main() {int n, m, x, y;cin >> n >> m >> x >> y;//全部置为-1,表示这些点没有访问过。for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {visit[i][j] = -1;}}queue queue;visit[x][y] = 0;queue.push( { x, y, 0 });//这样push也是一个结构体数据。while (!queue.empty()) {for (int i = 0; i < 8; i++) {int dx = queue.front().x + xx[i];int dy = queue.front().y + yy[i];if (dx >= 1 && dy >= 1 && dx <= n && dy <= m) {if (visit[dx][dy] == -1) {location temp = { x,y,0 };temp.x = dx;temp.y = dy;temp.s = queue.front().s + 1;//BFS的特性,其队列首部和尾部的一定是相邻步骤数,相差为1.queue.push(temp);visit[dx][dy] = temp.s;//记录马访问该点时的步骤数量。}}}queue.pop();}for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {cout << visit[i][j] << ' ';}cout << endl;}return 0;
}

相关内容

热门资讯

编程安卓系统和鸿蒙主题,跨平台... 你有没有想过,手机的世界里,除了苹果的iOS和安卓的操作系统,还有个神秘的鸿蒙系统?今天,咱们就来聊...
哪个安卓机系统好用,探索安卓系... 你有没有想过,手机里的安卓系统就像是个大厨,不同的系统就像不同的烹饪手法,有的让你吃得津津有味,有的...
安卓如何控制苹果系统,从安卓到... 你知道吗?在这个科技飞速发展的时代,安卓和苹果两大操作系统之间的较量从未停歇。虽然它们各自有着忠实的...
安卓原生系统文件夹,安卓原生系... 你有没有发现,每次打开安卓手机,里面那些文件夹就像是一个个神秘的宝箱,里面藏着各种各样的宝贝?今天,...
基于安卓系统的游戏开发,从入门... 你有没有想过,为什么安卓手机上的游戏总是那么吸引人?是不是因为它们就像是你身边的好朋友,随时随地都能...
安卓系统怎样装驱动精灵,安卓系... 你那安卓设备是不是突然间有点儿不给力了?别急,今天就来手把手教你如何给安卓系统装上驱动精灵,让你的设...
如何本地安装安卓系统包,详细步... 你有没有想过,把安卓系统装在你的电脑上,是不是就像给电脑穿上了时尚的新衣?想象你可以在电脑上直接玩手...
安卓12卡刷系统教程,体验全新... 你有没有发现,你的安卓手机最近有点儿不给力了?运行速度慢得像蜗牛,是不是也想给它来个“换血大法”,让...
安卓系统无法打开swf文件,安... 最近是不是发现你的安卓手机有点儿不给力?打开SWF文件时,是不是总是出现“无法打开”的尴尬局面?别急...
鸿蒙系统依赖于安卓系统吗,独立... 你有没有想过,我们手机里的那个鸿蒙系统,它是不是真的完全独立于安卓系统呢?这个问题,估计不少手机控都...
适合安卓系统的图片软件,精选图... 手机里堆满了各种美美的照片,是不是觉得找起来有点头疼呢?别急,今天就来给你安利几款超级适合安卓系统的...
阴阳师安卓系统典藏,探寻阴阳师... 亲爱的阴阳师们,你是否在安卓系统上玩得如痴如醉,对那些精美的典藏式神们垂涎欲滴?今天,就让我带你深入...
安卓系统有碎片化缺点,系统优化... 你知道吗?在手机江湖里,安卓系统可是个响当当的大侠。它那开放、自由的个性,让无数手机厂商和开发者都为...
安卓4系统手机微信,功能解析与... 你有没有发现,现在市面上还有很多安卓4系统的手机在使用呢?尤其是那些喜欢微信的朋友们,这款手机简直就...
鸿蒙系统是安卓的盗版,从安卓“... 你知道吗?最近在科技圈里,关于鸿蒙系统的讨论可是热闹非凡呢!有人说是安卓的盗版,有人则认为这是华为的...
安卓系统怎么剪辑音乐,轻松打造... 你是不是也和我一样,手机里存了超多好听的歌,但是有时候想给它们来个变身,变成一段专属的旋律呢?别急,...
怎么把安卓手机系统变为pc系统... 你有没有想过,把你的安卓手机变成一台PC呢?听起来是不是有点酷炫?想象你可以在手机上玩电脑游戏,或者...
手机怎么装安卓11系统,手机安... 你有没有想过,让你的手机也来个“青春焕发”,升级一下系统呢?没错,就是安卓11系统!这个新系统不仅带...
安卓系统如何拼网络,构建高效连... 你有没有想过,你的安卓手机是怎么和网络“谈恋爱”的呢?没错,就是拼网络!今天,就让我带你一探究竟,看...
安卓系统怎么看小说,轻松畅享电... 你有没有发现,手机里装了那么多应用,最离不开的竟然是那个小小的小说阅读器?没错,就是安卓系统上的小说...