数据结构C语言版算法大全

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

1) 插入操作
在顺序表L的第i (1<=L.length+1)个位置插入新元素e。

如果i的输入不合法,则返回false,表示插入失败;否则,将顺序表的第i个元素以及其后的元素右移一个位置,腾出一个空位置插入新元素e,顺序表长度增加1,插入成功,返回true。

1.bool ListInsert(SqList &L,int i, ElemType e){
2.//本算法实现将元素e插入到顺序表L中第i个位置
3.if( i<1|| i>L.length+1)
4.return false;// 判断i的范围是否有效
5.if(L.length>=MaxSize)
6.return false;// 当前存储空间已满,不能插入
7.for(int j =L.length; j >=i; j--)// 将第i个位置及之后的元素后移
8.L.data[j]=L.data[j-l];
9.L.data[i-1]=e;//在位置i处放入e
10.L.length++;//线性表长度加1
11.return true;
12.}
2) 删除操作
删除顺序表L中第i (1<=i<=L.length)个位置的元素,成功则返回true,否则返回false,并将被删除的元素用引用变量e返回。

复制纯文本新窗口
1.bool ListDelete(SqList &L,int i,int&e){
2.//本算法实现删除顺序表L中第i个位置的元素
3.if(i<1|| i>L.length)
4.return false;// 判断i的范围是否有效
5.e=L.data[i-1];// 将被删除的元素赋值给e
6.for(int j=i; j<L.length; j++)//将第i个位置之后的元素前移
7.L.data[j-1]=L.data[j];
8.L.length--;//线性表长度减1
9.return true;
10.}
3) 按值查找(顺序查找)
在顺序表L中查找第一个元素值等于e的元素,并返回其下标。

1.int LocateElem(SqList L, ElemType e){
2.//本算法实现查找顺序表中值为e的元素,如果查找成功,返回元素位序,否则
返回0
3.int i;
4.for(i=0;i<L.length;i++)
5.if(L.data[i]==e)
6.return i+1;// 下标为i的元素值等于e,返回其位号i+1
7.return0;//退出循环,说明查找失败
8.}
单链表的定义
1.typedef struct LNode{//定义单链表结点类型
2.ElemType data;//数据域
3.struct LNode *next;//指针域
4.}LNode,*LinkList;
采用头插法建立单链表
该方法从一个空表开始,生成新结点,并将读取到的数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头,即头结点之后,如图2-4所示。

图2-4 头插法建立单链表
头插法建立单链表的算法如下:
1.LinkList CreatList1(LinkList &L){
2.//从表尾到表头逆向建立单链表L,每次均在头结点之后插入元素
3.LNode *s;int x;
4.L=(LinkList)malloc(sizeof(LNode));//创建头结点
5.L->next=NULL;//初始为空链表
6.scanf("%d",&x);//输入结点的值
7.
8.while(x!=9999){//输入9999 表示结束
9.s=(LNode*)malloc(sizeof(LNode));//创建新结点
10.s->data=x;
11.s->next=L->next; //重点(如果使用头插法的话)
12.L->next=s;//将新结点插入表中,L为头指针
13.scanf("%d",&x);
14.}//while 结束
15.
16.return L;
17.}
采用尾插法建立单链表
头插法建立单链表的算法虽然简单,但生成的链表中结点的次序和输入数据的顺序不一致。

若希望两者次序一致,可采用尾插法。

该方法是将新结点插入到当前链表的表尾上,为此必须增加一个尾指针r,使其始终指向当前链表的尾结点,如图2-5所示。

图2-5 尾插法建立单链表
尾插法建立单链表的算法如下:
1.LinkList CreatList2(LinkList &L){
2.//从表头到表尾正向建立单链表L,每次均在表尾插入元素
3.int x;// 设元素类型为整型
4.L=(LinkList)malloc(sizeof(LNode));
5.LNode *s,*r=L;//r 为表尾指针
6.scanf("%d",&x);//输入结点的值
7.
8.while(x!=9999){//输入9999 表示结束
9.s=(LNode *)malloc(sizeof(LNode));
10.s->data=x; //重点
11.r->next=s;
12.r=s;//r指向新的表尾结点
13.scanf("%d",&x);
14.}
15.
16.r->next = NULL;//尾结点指针置空
17.return L;
18.}
按序号查找结点值
在单链表中从第一个结点出发,顺指针next域逐个往下搜索,直到找到第i个结点为止,否则返回最后一个结点指针域NULL。

按序号查找结点值的算法如下:
1.LNode GetElem(LinkList L,int i){
2.//本算法取出单链表L(带头结点)中第i个位置的结点指针
3.int j=1;//计数,初始为1
4.LNode *p = L->next;//头结点指针赋给p //注意
5.
6.if(i==0)
7.return L;//若i等于0,则返回头结点
8.if(i<1)
9.return NULL;//若i 无效,则返回NULL
10.
11.while( p && j<i ){//从第1个结点开始找,查找第i个结点
12.p=p->next;
13.j++;
14.}
15.
16.return p;//返回第i个结点的指针,如果i大于表长,p=NULL,直接返回p即

17.}
按值查找表结点
从单链表第一个结点开始,由前往后依次比较表中各结点数据域的值,若某结点数据域的值等于给定值e,则返回该结点的指针。

若整个单链表中没有这样的结点,则返回NULL。

按值查找结点的算法如下:
1.LNode LocateElem(LinkList L, ElemType e){
//本算法查找单链表L (带头结点)中数据域值等于e的结点指针,否则返回NULL
2.LNode *p=L->next;
3.while( p!=NULL && p->data!=e)//从第1个结点开始查找data域为e的结点
4.p=p->next;
5.
6.return p;//找到后返回该结点指针,否则返回NULL
7.}
插入结点操作
插入操作是将值为x的新结点插入到单链表的第i个位置上。

先检查插入位置的合法性,然后找到待插入位置的前驱结点,即第i-1个结点,再在其后插入新结点。

算法首先调用上面的按序号查找算法GetElem(L, i-1),查找第i-1个结点。

假设返回的第i-1个结点为*p,然后令新结,点*s的指针域指向*p的后继结点,再令结点*p的指针域指向新插入的结点*s。

其操作过程如图2-6所示。

图2-6 单链表的插入操作
实现插入结点的代码片段如下:
1.p=GetElem(L, i-1);// 语句①,查找插入位置的前驱结点
2.s->next=p->next;// 语句②,图2-6 中辑作步骤1
3.p->next=s;// 语句③,图2-6中操作步骤2
删除结点操作
删除操作是将单链表的第i个结点删除。

先检查删除位置的合法性,然后查找表中第i-1个结点,即被删结点的前驱结点,再将其删除。

其操作过程如图2-7所示。

图2-7 单链表结点的删除
假设结点*p为找到的被删结点的前驱结点,为了实现这一操作后的逻辑关系的变化,仅需修改*p的指针域,即将*p的指针域next指向*q的下一结点。

实现删除结点的代码片段如下:
1.p=GetElem(L,i-1);//查找删除位置的前驱结点
2.q=p->next;//令q指向被删除结点
3.p->next=q->next //将*q结点从链中“断开”
4.free(q);//释放结点的存储空间
和插入算法一样,该算法的主要时间也是耗费在查找操作上,时间复杂度为O(n)。

扩展:删除结点*p
要实现删除某一个给定结点*p,通常的做法是先从链表的头结点开始顺序找到其前驱结点,然后再执行删除操作即可,算法的时间复杂度为O(n)。

其实,删除结点*p的操作可以用删除*p的后继结点操作来实现,实质就是将其后继结点的值赋予其自身,然后删除后继结点,也能使得时间复杂度为O(1)。

实现上述操作的代码片段如下:
1.q=p->next;//令q 向*p的后继结点
2.p->data=p->next->data;//和后继结点交换数据域
3.p->next=q->next;//将*q结点从链中“断开”问题
4.free(q);//释放后继结点的存储空间
双链表
双链表中结点类型的描述如下:
1.typedef struct DNode {//定义双链表结点类型
2.ElemType data;//数据域
3.struct DNode *prior,*next;//前驱和后继指针
4.}DNode,*DLinklist;
双链表仅仅是在单链表的结点中增加了一个指向其前驱的prior指针,因此,在双链表中执行按值查找和按位查找的操作和单链表相同。

但双链表在插入和删除操作的实现上,和单链表有着较大的不同。

这是因为“链”变化时也需要对prior指针做出修改,其关键在于保证在修改的过程中不断链。

此外,双链表可以很方便地找到其前驱结点,因此,插入、删除结点算法的时间复杂度仅为O(1)。

双链表的插入操作
在双链表中p所指的结点之后插入结点*s,其指针的变化过程如图2-9所示。

插入操作的代码片段如下:
1.s->next=p->next;// 语句①,将结点*s插入到结点*p之后
2.p->next->prior=s;// 语句②
3.s->prior=p;// 语句③
4.p->next=s;// 语句④
图2-9 双链表插入结点过程
上述代码的语句顺序不是唯一的,但也不是任意的,①②两步必须在④步之前,否则*p 的后继结点的指针就丢掉了,导致插入失败。

为了加深理解,读者可以在纸上画出示意图。

若问题改成要求在结点*p之前插入结点*s,请读者思考具体的操作步骤。

双链表的删除操作
删除双链表中结点*p的后继结点*q,其指针的变化过程如图2-10所示。

图2-10 双链表删除结点过程
删除操作的代码片段如下:
1.p->next=q->next;// 图2-10中步骤①①
2.q->next->prior=p;//图2-10 中步骤②
3.free(q);//释放结点空间
栈的基本操作
各种辅导书中给出的基本操作的名称不尽相同,但所表达的意思大致是一样的。

这里我们以严蔚敏编写的教材为准给出栈的基本操作,希望读者能熟记下面的基本操作:
InitStack(&S):初始化一个空栈S。

StackEmpty(S):判断一个栈是否为空,若栈S为空返回true,否则返回false。

Push(&S, x):进栈,若栈S未满,将x加入使之成为新桟顶。

Pop(&S, &x):出栈,若栈S非空,弹出栈顶元素,并用x返回。

GetTop(S, &x):读栈顶元素,若栈S非空,用x返回栈顶元素。

ClearStack(&S):销毁栈,并释放栈S占用的存储空间。

注意:符号'&'是C++特有的,用来表示引用,有的书上釆用C语言中的指针类型‘*’,也可以达到传址的目的。

在解答算法题时,若题干没有做出限制,可以直接使用这些基本的操作函数。

1.栈的顺序存储结构
//----- 栈的顺序存储表示-----
#define STACK_INIT_SIZE 100;
#define STACKINCREMENT 10;
typedef struct {
SElemType *base;
SElemType *top;
int stacksize;
} SqStack;
Status InitStack (SqStack &S)
{// 构造一个空栈S
S.base=(ElemType*)malloc(STACK_INIT_SIZE* sizeof(ElemType));
if (!S.base) exit (OVERFLOW); //存储分配失败S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
return OK;
}
Status Push (SqStack &S, SElemType e) {
if (S.top - S.base >= S.stacksize) {//栈满,追加存储空间
S.base = (ElemType *) realloc ( S.base,(S.stacksize + STACKINCREMENT) *sizeof (ElemType));
if (!S.base) exit (OVERFLOW); //存储分配失败
S.top = S.base + S.stacksize;
S.stacksize += STACKINCREMENT;
}
*S.top++ = e;
return OK;
}
Status Pop (SqStack &S, SElemType &e) {
// 若栈不空,则删除S的栈顶元素,
// 用e返回其值,并返回OK;
// 否则返回ERROR
if (S.top == S.base) return ERROR;
e = *--S.top;
return OK;
}
链队列:
typedef struct QNode {// 结点类型
QElemType data;
struct QNode *next;
} QNode, *QueuePtr;
链队列——链式映象
typedef struct { // 链队列类型QueuePtr front; // 队头指针
QueuePtr rear; // 队尾指针
} LinkQueue;
Status InitQueue (LinkQueue &Q) {
// 构造一个空队列Q
Q.front = Q.rear =
(QueuePtr)malloc(sizeof(QNode));
if (!Q.front) exit (OVERFLOW);
//存储分配失败
Q.front->next = NULL;
return OK;
}
Status EnQueue (LinkQueue &Q, QElemType e) {
// 插入元素e为Q的新的队尾元素
p = (QueuePtr) malloc (sizeof (QNode));
if (!p) exit (OVERFLOW); //存储分配失败
p->data = e; p->next = NULL;
Q.rear->next = p; Q.rear = p;
return OK;
}
Status DeQueue (LinkQueue &Q, QElemType &e) {
// 若队列不空,则删除Q的队头元素,
//用e 返回其值,并返回OK;否则返回ERROR if (Q.front == Q.rear) return ERROR;
p = Q.front->next; e = p->data;
Q.front->next = p->next;
if (Q.rear == p) Q.rear = Q.front;
free (p); return OK;
}
循环队列:
#define MAXQSIZE 100 //最大队列长度typedef struct {
QElemType *base; // 动态分配存储空间
int front; // 头指针,若队列不空,
// 指向队列头元素
int rear; // 尾指针,若队列不空,指向
// 队列尾元素的下一个位置
} SqQueue;
循环队列——顺序映象
Status InitQueue (SqQueue &Q) {
// 构造一个空队列Q
Q.base = (ElemType *) malloc(MAXQSIZE *sizeof (ElemType)); if (!Q.base) exit (OVERFLOW);
// 存储分配失败
Q.front = Q.rear = 0;
return OK;
}
Status EnQueue (SqQueue &Q, ElemType e)
{ // 插入元素e为Q的新的队尾元素
if ((Q.rear+1) % MAXQSIZE == Q.front)
return ERROR; //队列满
Q.base[Q.rear] = e;
Q.rear = (Q.rear+1) % MAXQSIZE;
return OK;
}
Status DeQueue (SqQueue &Q, ElemType &e) { // 若队列不空,则删除Q的队头元素,
// 用e返回其值,并返回OK; 否则返回ERROR if (Q.front == Q.rear) return ERROR;
e = Q.base[Q.front];
Q.front = (Q.front+1) % MAXQSIZE;
return OK;
}
经典算法:
1.行编辑
while (ch != EOF) { //EOF为全文结束符
while (ch != EOF && ch != '\n') {
switch (ch) {
case '#' : Pop(S, c); break;
case '@': ClearStack(S); break;// 重置S为空栈
default : Push(S, ch); break;
}
ch = getchar(); // 从终端接收下一个字符}
2.
从原表达式求得后缀式的规律为:
1) 设立操作数栈;
2) 设表达式的结束符为“#”,
予设运算符栈的栈底为“#”;
3) 若当前字符是操作数,
则直接发送给后缀式。

4)若当前运算符的优先数高于栈顶
运算符,则进栈;
5) 否则,退出栈顶运算符发送给后
缀式;
6) “(” 对它之前后的运算符起隔离
作用,“)”可视为自相应左括弧开始
的表达式的结束符。

算法:
void transform(char suffix[ ], char exp[ ] ) { InitStack(S); Push(S, #);
p = exp; ch = *p;
while (!StackEmpty(S)) {
if (!IN(ch, OP)) Pass( Suffix, ch);
else { }
if ( ch!= # ) { p++; ch = *p; }
else { Pop(S, ch); Pass(Suffix, ch); }
} // while
} // CrtExptree
… …
switch (ch) {
case ( : Push(S, ch); break;
case ) : Pop(S, c);
while (c!= ( )
{ Pass( Suffix, c); Pop(S, c) }
break;
defult :
while(Gettop(S, c) && ( precede(c,ch)))
{ Pass( Suffix, c); Pop(S, c); }
if ( ch!= # ) Push( S, ch);
break;
} // switch
3.汉诺塔
void hanoi (int n, char x, char y, char z) {
// 将塔座x上按直径由小到大且至上而下编号为1至n
// 的n个圆盘按规则搬到塔座z上,y可用作辅助塔座。

1 if (n==1)
2 move(x, 1, z); // 将编号为1的圆盘从x移到z
3 else {
4 hanoi(n-1, x, z, y); // 将x上编号为1至n-1的
//圆盘移到y, z作辅助塔
5 move(x, n, z); // 将编号为n的圆盘从x移到z
6 hanoi(n-1, y, x, z); // 将y上编号为1至n-1的
//圆盘移到z, x作辅助塔
7 }
8 }
树:
二叉树的顺序存储:
#define MAX_TREE_SIZE 100 // 二叉树的最大结点数typedef TElemType SqBiTree[MAX_
TREE_SIZE]; // 0号单元存储根结点
SqBiTree bt;
二叉树的二叉链表存储:
typedef struct BiTNode { // 结点结构TElemType data;
struct BiTNode *l child, *r child;
// 左右孩子指针
} BiTNode, *BiTree;
二叉树的三叉链表存储:
typedef struct TriTNode { // 结点结构TElemType data;
struct TriTNode *l child, *r child;
// 左右孩子指针
struct TriTNode *parent; //双亲指针} TriTNode, *TriTree;
二叉树的双亲链表存储:
typedef struct BPTNode { // 结点结构TElemType data;
int *parent; // 指向双亲的指针
char LRT ag; // 左、右孩子标志域
} BPTNode
树的结构:
typedef struct BPTree{ // 树结构BPTNode nodes[MAX_TREE_SIZE];
int num_node; // 结点数目
int root; // 根结点的位置
} BPTree
树的先序遍历:
void Preorder (BiTree T,
void( *visit)(TElemType& e))
{ // 先序遍历二叉树
if (T) {
visit(T->data); // 访问结点
Preorder(T->l child, visit); // 遍历左子树
Preorder(T->r child, visit);// 遍历右子树
}
}
树的中序遍历递归算法:
void In order (BiTree T,void( *visit)(TElemType& e)) { // 中序遍历二叉树
if (T) {
Inorder(T->l child, visit); // 遍历左子树
visit(T->data); // 访问结点
Inorder(T->r child, visit);// 遍历右子树
}
}
树的中序遍历非递归算法:
BiTNode *GoFarLeft(BiTree T, Stack *S){ if (!T ) return NULL;
while (T->l child ){
Push(S, T);
T = T->l child;
}
return T;
}
void Inorder_I(BiTree T, void (*visit) (TelemType& e)){
Stack *S;
t = GoFarLeft(T, S); // 找到最左下的结点while(t){
visit(t->data);
if (t->r child)
t = GoFarLeft(t->r child, S);
else if ( !StackEmpty(S )) // 栈不空时退栈t = Pop(S);
else t = NULL; // 栈空表明遍历结束
} // while
}// Inorder_I
树的后序遍历:
void Baorder (BiTree T,
void( *visit)(TElemType& e))
{ // 后序遍历二叉树
if (T) {
Baorder(T->l child, visit); // 遍历左子树Baorder(T->r child, visit);// 遍历右子树visit(T->data); // 访问结点
}
}
统计二叉树中叶子节点的个数:
void CountLeaf (BiTree T, int& count){ if ( T ) {
if ((!T->l child)&& (!T->r child)) count++; // 对叶子结点计数CountLeaf( T->l child, count); CountLeaf( T->r child, count);
} // if
} // CountLeaf
统计二叉树的深度:
int Depth (BiTree T ){ // 返回二叉树的深度
if ( !T ) depthval = 0;
else {
depthLeft = Depth( T->l child );
depthRight= Depth( T->r child );
depthval = 1 + (depthLeft > depthRight ? depthLeft : depthRight);
}
return depthval;
}
复制二叉树:
生成一个二叉树的结点
(其数据域为item,左指针域为lptr,右指针域为rptr) BiTNode *GetTreeNode(TElemType item, BiTNode *lptr , BiTNode *rptr ){
if (!(T = (BiTNode*)malloc(sizeof(BiTNode)))) exit(1);
T-> data = item;
T-> l child = lptr; T-> r child = rptr;
return T;
}
BiTNode *CopyTree(BiTNode *T) {
if (!T ) return NULL;
if (T->l child )
newlptr = CopyTree(T->l child);//复制左子树
else newlptr = NULL;
if (T->r child )
newrptr = CopyTree(T->r child);//复制右子树
else newrptr = NULL;
newT = GetTreeNode(T->data, newlptr, newrptr); return newT;
} // CopyTree。

相关文档
最新文档