作为入门算法书,从各种实战的算法列举起,是一本既基础又实用的算法书籍,这里我截取几个重要的算法内容进行总结,作为这一阶段的回顾。
- 排序类:快速排序
在本文中作者基于快速排序思想给出了一种变式快排的形式,这里记录一下两者的区别。
1.实际的快排:快排总的来说是基准数的交换,即选取几个处于头部的基准数和两个探测哨兵,从队列的两端向内遍历,记录为a[0]、i和j。遍历的过程中,若发现j<a[0],则j与a[0]交换位置,若发现i>a[0],则i与a[0](已与j交换位置的a[0])交换位置,重复这个过程直到i==j。
2.啊哈算法里的快排:作者选取基准数在遍历过程中不变,只让i和j进行交换,最后a[0]再与i(j)进行交换,实际上这还是快排的思想,只是基准数我们放到最后进行交换罢了。
举例:有队列“6 1 2 7 9 3 4 5 10 8”
首先哨兵j开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵j先出动。哨兵j一步一步地向左挪动(即j--),直到找到一个小于6的数停下来。接下来哨兵i再一步一步向右挪动(即i++),直到找到一个数大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停在了数字7面前。
现在交换哨兵i和哨兵j所指向的元素的值。交换之后的序列如下。
6 1 2 5 9 3 4 7 10 8
到此,第一次交换结束。接下来开始哨兵j继续向左挪动。发现了4(比基准数6要小,满足要求)之后停了下来。哨兵i也继续向右挪动的,他发现了9(比基准数6要大,满足要求)之后停了下来。此时再次进行交换,交换之后的序列如下。
6 1 2 5 4 3 9 7 10 8
第二次交换结束,“探测”继续。哨兵j继续向左挪动,他发现了3(比基准数6要小,满足要求)之后又停了下来。哨兵i继续向右移动,糟啦!此时哨兵i和哨兵j相遇了,哨兵i和哨兵j都走到3面前。说明此时“探测”结束。我们将基准数6和3进行交换。交换之后的序列如下。
3 1 2 5 4 6 9 7 10 8
至此,我们将基准数6找到了,之后采用二分与递归的办法将 3 1 2 5 4 和 9 7 10 8进行又一轮递归。
代码如下:
#include <stdio.h>
/*unsigned int partion(unsigned int R[] , unsigned int low ,unsigned int high)
{
unsigned int swap,base;
swap=R[low];base=R[low];
while(low<high)
{
while(R[high]>=base&&low<high)
high--;
R[low]=R[high];
while(R[low]<=base&&low<high)
low++;
R[high]=base;swap=R[low];
R[low]=R[high];R[high]=swap;
}
return low;
}
void quick_sort(unsigned int ISBN[] ,unsigned int low ,unsigned int high)
{
unsigned int position;
if(low<high)
{
position=partion(ISBN , low , high);
quick_sort(ISBN , low , position-1);
quick_sort(ISBN , position+1 , high);
}
}
int main()
{
unsigned int ISBN[10]={20,40,32,67,40,20,89,300,400,15},low=0,high=9;
quick_sort(ISBN,low,high);
for(low=0;low<=high;low++)
printf("%d\n",ISBN[low]);
return 0;
}*/
- 栈、队列、链表:
(1)这可以说是数据结构里最常引入的概念了,关于栈,最先接触是单片机原理学习中提到的先进后出的概念,具体可以百度,这方面不难理解。使用结构体来构建栈的方法为:
struct stack
{int data[10]; int top; }; 其中top指向栈顶
(2)而队列则是一种先进先出的概念,类似于通过管道。下图中front指队列的头,rear指队列的尾。以结构体来实现的队列方法:struct queue
{ int data[1000]; int head; int tail; }; head指队头,tail指队尾。
以队列和栈的实验代码如下:
#include "stdio.h"
#define u8 unsigned char
/*struct queue
{
int data[1000];
int head;
int tail;
};
struct stack
{
int data[10];
int top;
};
int main()
{
u8 i,t,book[10]={0};
struct queue q1,q2;
struct stack s;
q1.head=1;q2.head=1;q1.tail=1;q2.tail=1;s.top=0;
//输入q1,q2数据
printf("请输入q1\n");
for(i=1;i<7;i++)
{
scanf("%d",&q1.data[q1.tail]);
q1.tail++;
}
printf("请输入q2\n");
for(i=1;i<7;i++)
{
scanf("%d",&q1.data[q1.tail]);
q1.tail++;
}
//每次抽取一张牌开始比较
while(q1.head!=q1.tail&&q2.head!=q2.tail)
{
t=q1.data[q1.head];
if(book[t]==0)
{
book[t]++;
s.top++;
q1.head++;
}
else
{
book[t]=0;
q1.head++;
q1.data[q1.tail]=t;
q1.tail++;
while(s.data[s.top]!=t)
{
q1.data[q1.tail]=s.data[s.top];
book[s.data[s.top]]=0;
s.top--;
q1.tail++;
}
q1.data[q1.tail]=s.data[s.top];
book[s.data[s.top]]=0;
s.top--;
q1.tail++;
}
t=q2.data[q2.head];
if(book[t]==0)
{
book[t]++;
s.top++;
q2.head++;
}
else
{
book[t]=0;
q2.head++;
q2.data[q2.tail]=t;
q2.tail++;
while(s.data[s.top]!=t)
{
q2.data[q2.tail]=s.data[s.top];
book[s.data[s.top]]=0;
s.top--;
q2.tail++;
}
q2.data[q2.tail]=s.data[s.top];
book[s.data[s.top]]=0;
s.top--;
q2.tail++;
}
}
if(q1.head!=q1.tail) printf("q2 win\n");
else printf("q1 win\n");
return 0;
}*/
(3)关于链表,应该说这是一种动态存储的有效手段,每一个节点由两部分组成:数据段和指针段,数据段用来存储节点的具体数值,指针段用来指向后续节点的地址。即在我们想要插入数值的时候,我们只需将中间的链表断开,k-1个的指针段指向我们想要插入的节点的地址,而我们想要插入的节点的指针段指向k节点的地址即可完成插入操作。
链表的添加和删除同理,我们只要改变地址域的指向即可实现,实在是非常方便。
- 搜索类:深度优先搜索和广度优先搜索。
(1)深度优先搜索:这两种方式可以说是遍历的一种极大应用,深度优先搜索的思想在于首先遍历一条可行线路,线路的构建一般由图来组成,遍历完可行线路后回到上一节点,再访问其余接地昂,构成另一条线路,直到所有的线路都被遍历过为止。通过这种方式,我们可以统计每一条路径经过的路径长短,对于最小路径来说是一种搜索方式。
深度优先搜索的一个重要方法就是调用递归,递归能够实现标记已走过的顶点功能,并回到上一顶点从而遍历所有路径。
举例而言:现有一副如上图的图,我们要进行遍历,采用深度优先搜索(DFS)的方式搜索的路径就是:
1->2->3->4->3->2->5
->2->1->8->6->7->6->8->9 (红字部分代表遍历过程中第一次经过的点)
实验代码如下:
#include <stdio.h>
#define u16 unsigned int
u16 x=1,y=1,a[6][5]={0},book[6][5]={0},step=0,min=999;
void dfs(u16 x,u16 y,u16 step)
{
u16 i,tx,ty;
u16 next[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
if(x==4&&y==3)
{
if(step<min)
min=step;
return;
}
for(i=0;i<=3;i++)
{
tx=x+next[i][0]; ty=y+next[i][1];
if(tx<1||ty<1||tx>5||tx>4)
continue;
if(book[tx][ty]==0&&a[tx][ty]==0)
{
book[tx][ty]=1;
dfs(tx,ty,step+1);
book[tx][ty]=0;
}
}
return;
}
int main()
{
u16 i,j,n,m;
printf("请输入行数n和列数m:");
scanf("%d %d",&n,&m);
printf("请输入迷宫矩阵:");
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
book[1][1]=1;
dfs(x,y,0);
printf("min=%d\n",min);
return 0;
}
(2)广度优先搜索(BFS):广度优先搜索的思想在于首先以一个未被访问过的顶点作为原始起点,访问其所有相邻的顶点,然后对每个相邻的顶点,再访问它们相邻的未被访问过的顶点,直到所有顶点都被访问过,遍历结束。
BFS的一个重要思路就是采用队列的方式来进行遍历。
具体实现过程:
实验过程代码:
#include "stdio.h"
#define u16 unsigned int
u16 book[20][20]={0},a[20][20]={0},sum=0,max=0,tx,ty,txmax,tymax;
struct queue
{
u16 x;
u16 y;
u16 step;
};
void total(u16 x,u16 y)
{
u16 i,j;
for(i=1;i<=11;i++)
{
if(a[x][i]==2)
sum++;
if(a[i][y]==2)
sum++;
}
if(sum>max)
{max=sum;txmax=tx;tymax=ty;}
sum=0;
}
int main()
{
struct queue que[500];
u16 i,j,n,m,head=1,tail=2,flag=0;
u16 next[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
que[head].x=3;que[head].y=3;que[head].step=0;
printf("请输入行数n和列数m:");
scanf("%d %d",&n,&m);
printf("请输入迷宫矩阵");
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
while(head<tail)
{
for(i=0;i<=3;i++)
{
tx=que[head].x+next[i][0];ty=que[head].y+next[i][1];
if(tx<1||tx>n||ty<1||ty>m)
continue;
if(a[tx][ty]==0&&book[tx][ty]==0)
{
book[tx][ty]=1;
que[tail].x=tx;que[tail].y=ty;que[tail].step=que[head].step+1;
tail++;
total(tx,ty);
}
}
head++;
}
printf("step=%d\n",que[tail-1].step);
printf("x=%d,y=%d\n",txmax,tymax);
printf("max=%d\n",max);
getchar(); getchar();
return 0;
}