数据结构-王道-线性表
线性表-链表
- 线性表的链式储存又称为链表,他是指通过一组任意的存储单元来存储线性表中的数据元素。
单链表
typedf struct LNode
{
ElemType data;
struct LNode *next;
}
-
因为单链表的数据元素是离散的分布在储存空间当中的,所以单链表是非随机存取的存取结构,即不能直接找到表中某个特定的的结点。查找某个特定的结点的时候需要从表头开始遍历,依次查找。
头结点和头指针的区别:不管带不带头结点,头指针始终指向链表的第一个结点,而头结点是带头结点链表中的第一个结点,结点内通常不储存信息。
引入头结点之后可以带来两个优点:
- 由于开始结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作和在表的其他位置上的操作一致,无需对第一个位置进行特殊的操作。
- 无论链表是否为空,其头指针是指向头结点的非空指针(空表中头结点的指针域为空),因此空表和非空表的处理也就统一了。
typedef struct LNode
{
int data;
struct LNode *next;
}LNode,*LinkList;
头插法建立单链表
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else /* __cplusplus */
#define NULL ((void *)0)
#endif /* __cplusplus */
#endif /* NULL */
typedef struct LNode
{
int data;
struct LNode *next;
}LNode, *LinkList;
LinkList CreateListHead(LinkList &L)
{
LNode *s;
int x;
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL;
scanf("%d",&x);
while(x!=9999)
{
s=(LinkList)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
scanf("%d",&x);
}
return L;
}
int main()
{
LinkList L;
CreateListHead(L);
printf("%d",L->next->data);
return 0;
}
尾插法建立单链表
LinkList CreateListHead(LinkList &L)
{
LNode *s;
int x;
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL;
scanf("%d",&x);
while(x!=9999)
{
s=(LNode*)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
scanf("%d",&x);
}
return L;
}
获取第i个元素的值
LinkList GetElem(LinkList L,int i)//
{
int j=0;
LinkList l=L;
if(i==0) // 要第0个就是头结点的地址了。
return L;
if(i<0) // 不存在负结点 好吧。
return NULL;
while(l&&j<i) // l 不为null
{
l=l->next;
j++;
}
return l;
}
双链表
双链表的定义
typedef struct DNode
{
int data;
struct DNode *prior,*next;
}DNode,*DLinkList;
书上没有要求但是还是写一个 双链表的头插法吧。
DLinkList CreateDListHead(DLinkList &L)
{
int x,count=0;
DLinkList s;
L = (DLinkList)malloc(sizeof(DNode));
L->prior=L->next=NULL;
scanf("%d",&x);
while(x!=9999)
{
if(count==0)
{
s=(DLinkList)malloc(sizeof(DNode));
s->data=x;
s->prior=L;
s->next=L->next;
L->next=s;
count++;
}
else
{
s=(DLinkList)malloc(sizeof(DNode));
s->data=x; // 头插法
s->prior=L; // 新加入的s的头指针 存放L(头结点)的地址
s->next=L->next; // s的尾指针存放L的后继结点。
L->next->prior=s; // L的后继结点的头指针存放s的地址
L->next=s; // ..
}
scanf("%d",&x);
}
return L;
}
int main()
{
DLinkList L;
CreateDListHead(L);
printf("%d\n",L->next->next->next->prior->data);
return 0;
}
静态链表 提一下好吧。
静态链表以\(next=-1\)作为结束标志。静态链表的插入,删除没什么好说的。为什么有这个鬼东西是因为:在一些不支持指针的高级语言(如Basic)中,这是一种巧妙的设计方法。
#define MaxSize 50
typedef struct
{
int data;
int next;
}SLinkList[MaxSize];
顺序表和链表的比较
-
存取方式
顺序表可以顺序存取,也可以随机存取,链表只能从表头顺序取元素。
欢迎使用马克飞象 -
逻辑结构和物理结构
采用顺序存储时,逻辑上相邻的元素,其对应的物理位置也相邻。而采用链式存储的时候,逻辑上相邻的元素在物理上并不一定相邻,其对应的逻辑关系是通过指针链接来表示的。
一定要搞清楚存取方式 和 储存方式。 -
查找,删除,插入操作。
对于按值查找来说,当顺序表在无序的情况下,两者的时间复杂度均为\(O(n)\);特别的当顺序表有序的时候可以采用折半查找的方式去查找\(O(\log_2n)\)。
对于按序号查找,顺序表支持随机访问,时间复杂度仅为\(O(1)\),而链表的平均时间复杂度为\(O(n)\)。顺序表的插入,删除操作,平均需要移动半个表长的元素。链表的插入删除操作,只需要修改相关结点的指针即可。因为链表带有指针域所以储存密度较低。 -
空间分配
顺序表在静态储存的情形下,一旦储存空间装满就不能扩充了,如果再加入新元素将出现内存溢出,需要实现分配足够大的空间,但是提前分配的太大了到时候可能又用不上。这个时候就需要动态储存出手了,虽然其储存空间可以扩充,但需要移动大量的元素,导致其操作效率太低,而且如果内存中没有更大块的连续空间的话,这时候扩充可能导致失败。链式储存的结点空间只在需要的时候申请分配,只要内存有空间就可以分配,不像前面的必须要连续,这样灵活而高效。
@(P38T21)
时间复杂度\(O(n)\),一边扫描就得到了想要的结果,这种狗题目容易想的是两边扫描的答案,但是这样的话 最高分十分,下面的可以吃满分也就是十五分。
int Search_k(LinkList L,int k)
{
LinkList l=L;// 存放倒数第k个的值。
bool flag = false;// 表示暂时没有找到。
int i=0;
while(L->next)
{
L=L->next;
i++;
if(i==k||flag)
{
flag=true;
l=l->next;
}
}
if(flag)
{
printf("%d\n",l->data);
return 1;
}
else
{
printf("%d\n",0);
return 0;
}
}
@(P38T22)
挺简单的以后这种题目尽量一次遍历就搞定。 时间复杂度\(O(1)\)
typedef struct LNode
{
int data;
struct LNode *next;
}LNode, *LinkList;
int ListLen(LinkList L)// 传入L但不加 取地址符号,不改变L原来的内容。
{
int len=0;
while(L->next)
{
len++;
L=L->next;
}
return len;
}
LinkList FindAddr(LinkList L,LinkList I)
{
int lenL,lenI;
lenI=ListLen(I);
lenL=ListLen(L);
if(lenI>lenL)
{
while(lenI-lenL)
{
lenI--;
I=I->next;
}
}
if(lenI<lenL)
{
while(lenL-lenI)
{
lenL--;
L=L->next;
}
}// 因为是找从哪里开始共同的后缀,但是两个链表不一定等长,所以将长的前面的剪掉就行了。
while(L->next!=NULL&&L->next!=I->next)
{
I=I->next;
L=L->next;
}
return L->next;//这个时候的L->next等于I->next。
}
@(p58T23)
typedef struct LNode
{
int data;
struct LNode *next;
}LNode, *LinkList;
void DeleSame(LinkList L,int n)
{
if(L->next==NULL)
printf("voidmei ¿ÕÁË£¿");
LinkList l;
l=L;
int store[n+1];
for(int i=0;i<=n;i++)
store[i] = 0;
while(l->next!=NULL)
{
int temp = l->next->data>0?l->next->data:-l->next->data;
if(store[temp]==0) // 如果这个数字(绝对值)没有出现过
{
store[temp]++; // 标记一下出现
l=l->next; // 跳过该结点,检查下一个结点。
}
else // 出现过:让下一个结点的下一个结点作为当前结点的下一个结点。
l->next=l->next->next; // 删除 该删除的结点。
}
}
LinkList CreateListHead(LinkList &L)
{
LNode *s;
int x;
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL;
scanf("%d",&x);
while(x!=9999)
{
s=(LNode*)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
scanf("%d",&x);
}
return L;
}
int main()
{
LinkList L,i;
CreateListHead(L);
DeleSame(L,22);
if(L->next==NULL)
printf("¿ÕÁË\n");
while(L->next!=NULL)
{
L=L->next;
printf("--------%d-----------------\n",L->data);
}
free(L);
return 0;
}