数据结构与算法

1. 绪论

1.1数据结构

  1. 数据结构三要素:逻辑、物理、数据的运算<br />
  2. 逻辑结构:集合、线性、树型、网状(图形)<br />
  3. 存储结构:顺序、链式、索引、散列<br />
  4. 数据结构的运算:<br />
  5. - a.原子类型:不可再分的类型。(bool,int)<br />
  6. - b.结构类型:可分解的类型(struct)<br />
  7. - c.抽象数据类型:定义了一个数据结构

1.2 算法

算法的特性:
a.有穷性(算法有穷,程序无穷) -b.确定性 -c.可行性 -d.输入与输出
好算法的特点:
a.正确性 -b.可读性 -c.健壮性 -d.高效率与低存储量
算法的效率:时间复杂度、空间复杂度
常对幂指阶:O(1)<O(log2n)<O(n)<O(nlog2n)<O(n^2) <O(n^3) <O(2^n) <O(n!)<O(n^n)

2.线性表

定义:具有相同数据类型的n个数据元素的有限序列.n为表长,若n=0,则线性表为空表;i为表的位序(从1开始)

a1为线性表的表头;an为线性表的表尾,除表头元素,表中每个元素有且仅有一个直接前驱,除表尾元素,表中每个元素有且仅有一个直接后继
线性表的特点:

  • 元素个数有限
  • 逻辑上的顺序性,元素有先后顺序
  • 元素为数据元素,每个元素都为单个元素
  • 元素数据类型相同,即每个元素占用的存储空间相同
    基本操作:

    1. 初始化、销毁
    2. 插入、删除
    3. 按值查找、按位查找
    4. 求表长、输出、判空

2.1顺序表

优点:可随机存取,存储密度高
缺点:要求大片连续空间,改变容量不方便
用顺序存储实现的线性表

  1. //静态表
  2. #include <iostream>
  3. using namespace std;
  4. #define MaxSize 10
  5. typedef struct{
  6. int data[MaxSize];
  7. int length;
  8. }SeqList;//静态列表
  9. void InitList(SeqList &L){
  10. for (int i=0;i<=MaxSize;i++)
  11. L.data[i]=0;
  12. L.length=0;
  13. }//静态列表初始化
  14. void ListInsert(SeqList &L,int i,int e){
  15. for(int j=L.length;j>=i;j--)
  16. L.data[j]=L.data[j-1];
  17. L.data[i-1]=e;
  18. L.length++;
  19. }//插入操作
  20. void ListDelete(SeqList &L,int i int &e){
  21. e=L.data[i-1];
  22. for (int j=i;j<=L.length;j++)
  23. L.data[j-1]=L.data[j];
  24. L.length--;
  25. }//删除操作
  26. void GetElem(SeqList L,int i){
  27. return L.data[i-1];
  28. }//按位查找
  29. void GetValue(SeqList L,int i,int e){
  30. for(int i =0;i<L.length;i++)
  31. if (L.data[i]==e)
  32. return i+1;
  33. }//按值查找
  34. int main()
  35. {
  36. SeqList L;
  37. InitList(L);
  38. int e=-1;
  39. ListInsert(L,1,3);
  40. ListInsert(L,2,4);
  41. ListInsert(L,3,5);
  42. ListInsert(L,8,7);
  43. ListInsert(L,9,6);
  44. for (int i=0;i<MaxSize;i++)
  45. printf("%d,%d\n",L.data[i],L.length);
  46. return 0;
  47. }
  48. //3,5
  49. //4,5
  50. //5,5
  51. //0,5
  52. //0,5
  53. //0,5
  54. //0,5
  55. //7,5
  56. //6,5
  57. //0,5
  1. #include <iostream>
  2. using namespace std;
  3. #include <stdlib.h>
  4. #define InitSize 10
  5. typedef struct{
  6. int *data;
  7. int MaxSize;
  8. int length;
  9. }SeqList;//定义线性表
  10. void InitList(SeqList &L){
  11. L.data=(int *)malloc(InitSize*sizeof(int));//L.data=new int[InitSize];
  12. L.length=0;
  13. L.MaxSize=InitSize;
  14. }//初始化动态表
  15. //其余操作与静态表相似
  16. int main(){
  17. SeqList L;
  18. InitList(L);
  19. return 0;
  20. }

2.2链表(链式存储)

2.2.1单链表

定义:用链式存储的方式实现了线性的结构。结点(数据结构+指针)
优点:不要求大片连续空间,改变容量方便
缺点:不可随机存取,要消耗一定空间存放指针

  1. //不带头节点
  2. #include <iostream>
  3. using namespace std;
  4. typedef struct LNode{
  5. int data;//每个节点存放数据元素
  6. struct Lnode *next;//指针指向下一节点
  7. }LNode,*LinkList;
  8. bool InitList(LinkList &L){
  9. L=NULL;//空表
  10. return true;
  11. }
  12. bool Empty(LinkList L){
  13. return (L==NULL);
  14. }
  15. void test(){
  16. LinkList L;//声明指向单链表的指针
  17. InitList(L);//初始化空表
  18. }
  1. //带头结点
  2. bool InitList(LinkList &L){
  3. L=(LNode *)malloc(sizeof(LNode));//分配头结点
  4. if (L==NuLL)//内存不足
  5. return false;
  6. L->next =NULL;//头结点后暂时没有结点
  7. return true;
  8. }
  9. bool Empty(LinkList L){
  10. if(L->next==NULL)
  11. return true;
  12. else
  13. return false;
  14. }

带头结点——按位插入

  1. bool InsertNextNOde(LNode *p,ElemType e){
  2. if (p==NULL)
  3. return false;
  4. LNode *S=(LNode *)malloc(sizeof(LNode));
  5. if(S==NULL)
  6. return false;
  7. s->data=e;
  8. s->next=p->next;
  9. p->next=s;
  10. return true;
  11. }//后插操作
  12. bool ListInsert(ListNode &L,int i ,ElempType e){
  13. if (i<1)//i表示位序,所以不能小于1
  14. return false;
  15. LNode *p;//指针扫描当前节点
  16. int j=0;//当前p指向的结点
  17. p=L;//L指向头结点,头结点是第0个结点(不存放数据)
  18. while (p!=NULL && j<i-1){
  19. p=p->next;
  20. j++;
  21. }
  22. return InsertNextNode(p,e);//结点p后插入数据
  23. }

不带头结点

  1. bool ListInsert(ListNode &L,int i ,ElempType e){
  2. if (i<1)//i表示位序,所以不能小于1
  3. return false;
  4. if (i=1){
  5. LNode=(LNode *)malloc(sizeof(LinkLIst));
  6. s->data=e;
  7. s->next=L;
  8. L=s;
  9. return true;
  10. }
  11. LNode *p;//指针扫描当前节点
  12. int j=1;//当前p指向的结点
  13. p=L;//L指向头结点,头结点是第0个结点(不存放数据)
  14. while (p!=NULL && j<i-1){
  15. p=p->next;
  16. j++;
  17. }
  18. return InsertNextNode(p,e);
  19. }
  1. //前插操作
  2. bool InsertPriorNode(LNode *p,ElemType){
  3. if (p==NULL)
  4. return false;
  5. LNode *s=(LNode *)malloc(sizeof(LNode));
  6. if (s==NULL)
  7. return false;
  8. s->next=p->next;//新节点指向原结点后的数据
  9. p->next=s;//原结点指向新节点
  10. s->data=p->data;//原结点数据赋值给新节点
  11. p->data=e;//原结点保存插入的数据
  12. return true;
  13. }
  1. //删除操作-按位删除(带头结点)
  2. bool ListDelete(LinkList &L,int i,ElemType &e){
  3. if (i<1)
  4. return false;
  5. LNode *p;
  6. int j=0;
  7. p=L;
  8. while(p!=NULL && j<i-1){
  9. p=p->next;
  10. j++;
  11. }
  12. if (p==NULL)
  13. return false;
  14. LNode *q=p->next;
  15. e=q->data;//返回删除的值
  16. p->next=q->next;
  17. free(q);
  18. return true;
  19. }

删除制定节点P,但是删除表尾元素会导致空指针

  1. bool InsertPriorNode(LNode *p,ElemType){
  2. if (p==NULL)
  3. return false;
  4. LNode *s=(LNode *)malloc(sizeof(LNode));
  5. if (s==NULL)
  6. return false;
  7. s->next=p->next;//新节点指向原结点后的数据
  8. if(p->next!=NULL)
  9. p->next->prio=s;
  10. s->prio=p;
  11. p->next=s;
  12. return true;
  13. }
  1. //按位查找
  2. LNode * GetElem(LinkList L,int i){
  3. if (i<0)
  4. return NULL;
  5. LNode *p;
  6. int j=0;
  7. p=L;
  8. while (p==NULL && j<i){
  9. p=p->next;
  10. j++;
  11. }
  12. return p;
  13. }

2.2.2双链表

相比单链表,多了一个指向前驱元素的指针,在修改时也要注意修改前驱元素指针

  1. //简单举例
  2. bool DeleteNode(LNode *p){
  3. if (p==NULL)
  4. return false;
  5. LNode *q=p->next;
  6. }
  7. //删除操作简单思路
  8. //删除p的后继结点q
  9. if (p==NULL) return false;
  10. if (q==NULL) return false;
  11. p->next=q->next;//q结点指向的指针让p结点指定
  12. if(q->next !=NULL)
  13. q->next->prio=p;//q结点后的前指针,指向p,使得q没有被任何结点指向
  14. free(q);//释放q空间

2.2.3循环单链表/循环双链表

在其代码逻辑上与单/双链表毫无区别,主要不同表现在二者表尾/表头或这前驱插入时,在删除时也要将表尾指向表头的指针进行修改

2.2.4静态链表

  1. #define MaxSize 10
  2. typedef struct {
  3. int data;
  4. int next;
  5. }SLinkList[MaxSize];

思路:初始化时,头结点的指针指向-1,其余的next指向-2;
插入/删除,修改next指针

2.3.顺序表与链表的比较

顺序表:随机存取,存储密度大,大片连续分配空间不方便,改变容量不便
销毁:静态分配:系统自动回收
动态分配:手动(free)
链式表:改变容量方便,不可随机存取,存储密度低

开放式答题模版:

1.顺序表与链表的逻辑结构都是线性结构,都属于线性表

2.但是二者存储结构不同,顺序表采用顺序存储(优点、缺点 ),链表采用链式存储(优点、缺点)

3.由于采用不同的存储结构,基础操作实现的效率也有所不同

  • 初始化:….
  • 插入/删除元素:…..
  • 查找元素:…….