数据结构复习总结

一、链表

链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。(摘自百度)

链表结构图:

链表的C语言描述(摘自 数据结构C语言版-严蔚敏):

描述单链表:

1 typedef  struct  Lnode
2 {   ElemType  data;     /*数据域,保存结点的值 */
3 struct   Lnode  *next;      /*指针域*/
4 }LNode;        /*结点的类型 */

建立单链表:头插法:

 1 LNode  *create_LinkList(void)
 2     /*  头插入法创建单链表,链表的头结点head作为返回值  */
 3 {    int data ;
 4 LNode *head, *p;
 5 head= (LNode  *) malloc( sizeof(LNode));
 6 head->next=NULL;       /*  创建链表的表头结点head  */
 7 while (1)
 8 {   scanf(“%d”, &data) ;
 9 if (data==32767)  break ;
10 p= (LNode  *)malloc(sizeof(LNode));
11 p–>data=data;     /*  数据域赋值  */
12 p–>next=head–>next ;  head–>next=p ;
13        /*  钩链,新创建的结点总是作为第一个结点  */
14 }
15 return (head);
16 }

建立链表:尾插法:

 1 LNode  *create_LinkList(void)
 2      /*  尾插入法创建单链表,链表的头结点head作为返回值  */
 3 {   int data ;
 4 LNode *head, *p, *q;
 5 head=p=(LNode  *)malloc(sizeof(LNode));
 6 p->next=NULL;        /*  创建单链表的表头结点head  */
 7 while (1)
 8 {    scanf(“%d”,& data);
 9 if (data==32767)  break ;
10 q= (LNode  *)malloc(sizeof(LNode));
11 q–>data=data;     /*   数据域赋值  */
12 q–>next=p–>next;  p–>next=q; p=q ;
13  /*钩链,新创建的结点总是作为最后一个结点*/
14 }
15 return (head);
16 }

查找节点(按值查找):

 1 LNode *Locate_Node(LNode *L,int key)
 2 /*  在以L为头结点的单链表中查找值为key的第一个结点  */
 3 {   LNode *p=L–>next;
 4 while  ( p!=NULL&& p–>data!=key)    p=p–>next;
 5 if  (p–>data==key)   return p;
 6 else
 7 {    printf(“所要查找的结点不存在!!\n”);
 8 retutn(NULL);
 9 }
10 }

插入节点(在第i位置插入值为e的节点):

 1 void  Insert_LNode(LNode *L,int i,ElemType e)
 2     /*  在以L为头结点的单链表的第i个位置插入值为e的结点 */
 3 {   int  j=0;  LNode *p,*q;
 4 p=L–>next ;
 5 while  ( p!=NULL&& j<i-1)
 6 {  p=p–>next;  j++;   }
 7 if  (j!=i-1)     printf(“i太大或i为0!!\n ”);
 8 else
 9 {  q=(LNode *)malloc(sizeof(LNode));
10 q–>data=e;   q–>next=p–>next;
11 p–>next=q;
12 }
13 }

删除节点(按序号):

 1 void  Delete_LinkList(LNode *L, int i)
 2   /*  删除以L为头结点的单链表中的第i个结点  */
 3 {  int  j=1;  LNode *p,*q;
 4 p=L;  q=L->next;
 5 while  ( p->next!=NULL&& j<i)
 6 {  p=q;  q=q–>next;  j++;  }
 7 if  (j!=i)     printf(“i太大或i为0!!\n ”);
 8 else
 9 {  p–>next=q–>next;   free(q);    }
10 }

删除节点(按值):

 1 void  Delete_LinkList(LNode *L,int key)
 2 /*  删除以L为头结点的单链表中值为key的第一个结点  */
 3 {     LNode *p=L,  *q=L–>next;
 4 while  ( q!=NULL&& q–>data!=key)
 5 {  p=q;  q=q–>next;   }
 6 if  (q–>data==key)
 7 {  p->next=q->next;  free(q);   }
 8 else
 9 printf(“所要删除的结点不存在!!\n”);
10 } 

删除节点(删除所有值为key的节点):

思路:从单链表的第一个结点开始,对每个结点进行检查,若结点的值为key,则删除之,然后检查下一个结点,直到所有的结点都检查。

 1 void  Delete_LinkList_Node(LNode *L,int key)
 2 /*  删除以L为头结点的单链表中值为key的第一个结点  */
 3 {     LNode *p=L,  *q=L–>next;
 4 while  ( q!=NULL)
 5 {  if (q–>data==key)
 6      {  p->next=q->next;  free(q);  q=p->next;  }
 7 else
 8      {  p=q;  q=q–>next;   }
 9 }
10 } 

删除节点(删除所有重复节点):

基本思路:从单链表的第一个结点开始,对每个结点进行检查:检查链表中该结点的所有后继结点,只要有值和该结点的值相同,则删除之;然后检查下一个结点,直到所有的结点都检查。

 1 void  Delete_Node_value(LNode *L)
 2 /*  删除以L为头结点的单链表中所有值相同的结点  */
 3 {     LNode *p=L->next, *q, *ptr;
 4 while  ( p!=NULL)   /*  检查链表中所有结点  */
 5 {   *q=p, *ptr=p–>next;
 6 /*  检查结点p的所有后继结点ptr  */
 7 while (ptr!=NULL)
 8      {  if (ptr–>data==p->data)
 9             {  q->next=ptr->next;  free(ptr);
10                 ptr=q->next;  }
11         else  {  q=ptr;  ptr=ptr–>next;   }
12      }
13         p=p->next ;
14     }
15 }
16     

双向链表

定义:

指的是构成链表的每个结点中设立两个指针域:一个指向其直接前趋的指针域prior,一个指向其直接后继的指针域next。这样形成的链表中有两个方向不同的链,故称为双向链表。

双向链表是为了克服单链表的单向性的缺陷而引入的。

单个节点:

C语言描述:

1 typedef struct Dulnode
2 {     ElemType  data ;
3 struct Dulnode  *prior , *next ;
4 }DulNode ;

双链表基本操作:

插入:

结构图过程描述

1.插入时仅仅指出直接前驱结点,钩链时必须注意先后次序是: “先右后左” 。

C语言描述:

1 S=(DulNode *)malloc(sizeof(DulNode));
2 S->data=e;
3 S->next=p->next;   p->next->prior=S;
4 p->next=S;  S->prior=p;    /*  钩链次序非常重要  */

2.插入时同时指出直接前驱结点p和直接后继结点q,钩链时无须注意先后次序。部分语句组如下:

1 S=(DulNode *)malloc(sizeof(DulNode));
2 S->data=e;
3 p->next=S;       S->next=q;
4 S->prior=p;         q->prior=S; 

删除:

在双向链表中插入和删除必须同时修改两个方向上的指针域的指向。

p->prior->next=p->next;
p->next->prior=p->prior;
free(p);

二叉树

定义:

二叉树(Binary tree)是n(n≥0)个结点的有限集合。若n=0时称为空树,否则:

⑴ 有且只有一个特殊的称为树的根(Root)结点;

⑵ 若n>1时,其余的结点被分成为二个互不相交的子集T1,T2,分别称之为左、右子树,并且左、右子树又都是二叉树。

由此可知,二叉树的定义是递归的。

存储结构(链式,这里不讨论顺序存储结构):

C语言描述:

1 typedef struct BTNode
2 {  ElemType  data ;
3 struct BTNode  *Lchild , *Rchild ;
4 }BTNode ; 

三叉链表结构:除二叉链表的三个域外,再增加一个指针域,用来指向结点的父结点
结构图:

C语言描述:

1 typedef struct BTNode_3
2 {  ElemType  data ;
3 struct BTNode_3  *Lchild , *Rchild , *parent ;
4 }BTNode_3 ; 

二叉树的链式存储结构:

二叉树的遍历:

若以L、D、R分别表示遍历左子树、遍历根结点和遍历右子树,则有六种遍历方案:DLR、LDR、LRD、DRL、RDL、RLD。若规定先左后右,则只有前三种情况三种情况,分别是:

DLR——先(根)序遍历。

LDR——中(根)序遍历。

LRD——后(根)序遍历。

如图所示的二叉树表示表达式:(a+b*(c-d)-e/f)

按不同的次序遍历此二叉树,将访问的结点按先后次序排列起来的次序是:

其先序序列为:  -+a*b-cd/ef

其中序序列为:   a+b*c-d-e/f

其后序序列为:   abcd-*+ef/-

先序遍历(递归):

算法的递归定义是:

若二叉树为空,则遍历结束;否则

⑴ 访问根结点;

⑵ 先序遍历左子树(递归调用本算法);

⑶ 先序遍历右子树(递归调用本算法)。

1 void  PreorderTraverse(BTNode  *T)
2 {  if  (T!=NULL)
3 {  visit(T->data) ;       /*  访问根结点  */
4 PreorderTraverse(T->Lchild) ;
5 PreorderTraverse(T->Rchild) ;
6 }
7 }

说明:visit()函数是访问结点的数据域,其要求视具体问题而定。

先序遍历(非递归):

设T是指向二叉树根结点的指针变量,非递归算法是:

若二叉树为空,则返回;否则,令p=T;

⑴ 访问p所指向的结点;

⑵ q=p->Rchild ,若q不为空,则q进栈;

⑶ p=p->Lchild ,若p不为空,转(1),否则转(4);

⑷  退栈到p ,转(1),直到栈空为止。

 1 #define  MAX_NODE  50
 2 void  PreorderTraverse( BTNode  *T)
 3 {  BTNode  *Stack[MAX_NODE] ,*p=T, *q ;
 4 int  top=0 ;
 5 if  (T==NULL)  printf(“ Binary Tree is Empty!\n”) ;
 6 else {  do
 7       {  visit( p-> data ) ;   q=p->Rchild ;
 8           if  ( q!=NULL )  stack[++top]=q ;
 9           p=p->Lchild ;
10           if (p==NULL) { p=stack[top] ;  top-- ; }
11       }
12    while (p!=NULL) ;
13 }
14 }

中序遍历(递归)

算法的递归定义是:

若二叉树为空,则遍历结束;否则

⑴ 中序遍历左子树(递归调用本算法);

⑵ 访问根结点;

⑶ 中序遍历右子树(递归调用本算法)。

1 void  InorderTraverse(BTNode  *T)
2 {  if  (T!=NULL)
3 {  InorderTraverse(T->Lchild) ;
4 visit(T->data) ;       /*   访问根结点   */
5 InorderTraverse(T->Rchild) ;
6 }
7 }   /*图6-8(a) 的二叉树,输出的次序是: cbegdfa */

中序遍历(非递归)

设T是指向二叉树根结点的指针变量,非递归算法是:

若二叉树为空,则返回;否则,令p=T

⑴ 若p不为空,p进栈, p=p->Lchild ;

⑵ 否则(即p为空),退栈到p,访问p所指向的结点;

⑶ p=p->Rchild ,转(1);

直到栈空为止。

 1 #define MAX_NODE  50
 2 void  InorderTraverse( BTNode  *T)
 3 {  BTNode  *Stack[MAX_NODE] ,*p=T ;
 4     int  top=0 , bool=1 ;
 5     if  (T==NULL)  printf(“ Binary Tree is Empty!\n”) ;
 6    else  { do
 7                  { while (p!=NULL)
 8                         {  stack[++top]=p ;  p=p->Lchild ;   }
 9                      if  (top==0)  bool=0 ;
10                      else  {  p=stack[top] ;  top-- ;
11                                  visit( p->data ) ;  p=p->Rchild ; }
12                  }  while (bool!=0) ;
13            }
14  }

后序遍历(递归)

算法的递归定义是:

若二叉树为空,则遍历结束;否则

⑴ 后序遍历左子树(递归调用本算法);

⑵ 后序遍历右子树(递归调用本算法) ;

⑶ 访问根结点 。

1 void  PostorderTraverse(BTNode  *T)
2 {  if  (T!=NULL)
3 {  PostorderTraverse(T->Lchild) ;
4 PostorderTraverse(T->Rchild) ;
5 visit(T->data) ;       /*  访问根结点  */
6 }
7 }   /*图6-8(a) 的二叉树,输出的次序是: cgefdba   */

遍历二叉树的算法中基本操作是访问结点,因此,无论是哪种次序的遍历,对有n个结点的二叉树,其时间复杂度均为O(n) 。

后续遍历(非递归)

在后序遍历中,根结点是最后被访问的。因此,在遍历过程中,当搜索指针指向某一根结点时,不能立即访问,而要先遍历其左子树,此时根结点进栈。当其左子树遍历完后再搜索到该根结点时,还是不能访问,还需遍历其右子树。所以,此根结点还需再次进栈,当其右子树遍历完后再退栈到到该根结点时,才能被访问。

因此,设立一个状态标志变量tag :

其次,设两个堆栈S1、S2 ,S1保存结点,S2保存结点的状态标志变量tag 。S1和S2共用一个栈顶指针。

设T是指向根结点的指针变量,非递归算法是:

若二叉树为空,则返回;否则,令p=T;

⑴ 第一次经过根结点p,不访问:

p进栈S1 , tag 赋值0,进栈S2,p=p->Lchild 。

⑵ 若p不为空,转(1),否则,取状态标志值tag :

⑶ 若tag=0:对栈S1,不访问,不出栈;修改S2栈顶元素值(tag赋值1) ,取S1栈顶元素的右子树,即p=S1[top]->Rchild ,转(1);

⑷ 若tag=1:S1退栈,访问该结点;

直到栈空为止。

C语言描述(后续遍历非递归):

 1 #define MAX_NODE  50
 2 void  PostorderTraverse( BTNode  *T)
 3 {  BTNode  *S1[MAX_NODE] ,*p=T ;
 4 int S2[MAX_NODE] , top=0 , bool=1 ;
 5 if  (T==NULL)  printf(“Binary Tree is Empty!\n”) ;
 6 else  { do
 7      {   while (p!=NULL)
 8               {  S1[++top]=p ; S2[top]=0 ;
 9                   p=p->Lchild ;
10               }
11          if  (top==0)  bool=0 ;
12        else if  (S2[top]==0)
13                   {  p=S1[top]->Rchild ;  S2[top]=1 ;   }
14               else
15                    {  p=S1[top] ;  top-- ;
16                        visit( p->data ) ; p=NULL ;
17                         /*  使循环继续进行而不至于死循环 */                       }
18 }  while (bool!=0) ;
19 }
20 }

层次遍历:

层次遍历二叉树,是从根结点开始遍历,按层次次序“自上而下,从左至右”访问树中的各结点。

为保证是按层次遍历,必须设置一个队列,初始化时为空。

设T是指向根结点的指针变量,层次遍历非递归算法是:

若二叉树为空,则返回;否则,令p=T,p入队;

⑴ 队首元素出队到p;

⑵访问p所指向的结点;

⑶将p所指向的结点的左、右子结点依次入队。直到队空为止。

C语言描述(层次遍历非递归):

 1 #define MAX_NODE  50
 2 void  LevelorderTraverse( BTNode  *T)
 3 {  BTNode  *Queue[MAX_NODE] ,*p=T ;
 4 int  front=0 , rear=0 ;
 5 if  (p!=NULL)
 6 {  Queue[++rear]=p;    /*   根结点入队  */
 7 while (front<rear)
 8      {  p=Queue[++front];  visit( p->data );
 9          if (p->Lchild!=NULL)
10                Queue[++rear]=p;    /*   左结点入队  */
11          if (p->Rchild!=NULL)
12                Queue[++rear]=p;    /*   左结点入队  */
13       }
14 }
15 }
时间: 2024-11-13 10:46:33

数据结构复习总结的相关文章

数据结构复习之--“插入排序”-JAVA实现

菜鸟中的菜鸟一枚,面临找工作,复习下数据结构,写的插入排序算法记录一下,每天写一点,不断积累吧! import java.util.Scanner; /** * * @author DL * 数据结构复习之插入排序练习程序 * */ public class SortTest { public static void main(String [] args){ Scanner sc = new Scanner(System.in); int totalNums = sc.nextInt();//

数据结构复习之C语言指针与结构体

数据结构指针复习: #include <stdio.h> void main() { int a[5] = {8, 2, 3, 4, 5}; // a[3] == *(3+a) printf("%d\n", *(3+a)); // a[3] 4 printf("*a其实就是a[0]: %d\n", *a); // 8 // 地址是连续的 printf("%p\n", a+1); printf("%p\n", a+2

数据结构复习笔记--数组

最后还是决定在未来的道路上走向软件开发者这条路,从现在重新复习数据结构和算法. 关于数组有几个比较有意思的特点. 1.对于数组 int List[3],编译器将List[i]解释为指向一个地址为List + i*sizeof(int)的整数的指针. 对于int * List, int *List2[5],两个都是指向int 类型的变量,但是编译器会为后者分配五个整数存储空间. List2实际是上指向List2[0],List2 + i 实际上是&List2[i].在C语言中是不需要加上偏移量的.

数据结构复习之开题篇(必要时持续更新...)

在复习数据结构之前有必要复习一下C语言的指针 1.指针 int* p; 一个指针类型,名为p,代表的是一个整形所存放的地址编号 1.1一个使用的实例 [注]对变量使用&操作符,意为取它所在的地址 1 #include <stdio.h> 2 3 int main() 4 { 5 int var = 20; /* 实际变量的声明 */ 6 int* ip; /* 指针变量的声明 */ 7 8 ip = &var; /* 在指针变量中存储 var 的地址 */ 9 10 print

数据结构复习---顺序表和单链表

1.前言: 最近比较浮躁,想学习一门新的技术却总是浅尝辄止,遇到不懂的地方就想跳过去,时间长了,心态就有点崩了.静下心来,去喝了几碗心灵鸡汤.鸡汤博主感动到了我:"无专注,无风景.不要太贪心,一次只做一件事,而且只做最重要的事.".于是乎,我把家里翻了个底朝天,找到了我垫在床底下的<数据结构>这本书,觉得自己是时候静下心来好好复习一下基础了.今天刚看到顺序表和链表,把我的学习心得记录在这里.也希望自己能坚持,老老实实的把这本书复习完. 2.数据结构的重要性: 讲一门知识之前

数据结构复习2

1. 一个非空广义表的表尾(只能是子表) 解析:(1)<数据结构>对广义表的表头和表尾是这样定义的:  如果广义表LS=(a1,a2...an)非空,则 a1是LS的表头,其余元素组成的表(a2,a3,..an)是称为LS的表尾.  根据定义,非空广义表的 表头是一个元素,它 可以是原子也可以是一个子表, 而表尾则必定是子表.例如:LS=(a,b),表头为a,表尾是(b)而不是b.另外:LS=(a)的表头为a,表尾为空表(). (2)非空广义表,除表头外,其余元素构成的表称为表尾,所以非空广义

《数据结构复习笔记》--二叉搜索树

二叉搜索树:维基百科:click here 二叉查找树(Binary Search Tree),也称二叉搜索树.有序二叉树(ordered binary tree),排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树: 若任意节点的左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值: 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值: 任意节点的左.右子树也分别为二叉查找树. 没有键值相等的节点(no duplicate nodes

数据结构复习_队列、循环对列

类别    数据结构 1.主题 2.学习视频和资料 视频及课件    http://www.icourse163.org/learn/zju-93001?tid=120001#/learn/content?type=detail&id=398005 3.队列的基本操作 定义队列元素 建立队列信息(队首和队尾标记,总长度) 初始化队列 销毁队列 插入(只能在尾部,判断是否溢出)(入队) 删除(只能在头部,判断对列是否为空)(出队) 4.存储(实现结构) 顺序存储--连续内存,如数组 链式存储 5.

数据结构复习

1.         以Niklus Wirth的观点,程序等于什么? =数据结构+算法 2.         算法的重要特性:确定.有穷.能行.输入.输出 3.         好算法的标准:正确.可读.健壮.高效低存贮 4.         数据结构主要研究对象:逻辑结构.存贮结构和运算(增删改查) 5.         数据的逻辑结构有几大类?(线性.非线性) 6.         数据的存贮结构有几类?(顺序.链式.索引.散列hash) 7.         对数据的最主要的操作:增删改

数据结构复习之散列表查找(哈希表)

一.散列表相关概念 散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key).公式如下: 存储位置 = f(关键字) 这里把这种对应关系f称为散列函数,又称为哈希(Hash)函数.按这个思想,采用散列技术将记录存在在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表.那么,关键字对应的记录存储位置称为散列地址. 散列技术既是一种存储方法也是一种查找方法.散列技术的记录之间不存在什么逻辑关系,它只与关键字有关,因此,散列主要是面向查