数据结构之线性表:
有序表:数组:
单链表:
链表定义  
{  
- 数据成员:常见的基本类型或者对象类型均可
- 数据关系:在链表中的块的成员在连续的内存中,块与块之间内存位置不连续
- 指向块的指针:单链表只有一个next,双链表加上pre
}
基本运算:
{
- InitList(&L);:初始化链表,在c/c++中需要做分配内存,初始化链表头和其值的操作;
- DestroyList(&L); 在销毁时需要free内存
- Length(L);链表的长度是块的个数
- GetElem(L,i,&e); 在链表中取元素需要一个一个遍历直到下标值,若为双链表会快些。所以这里取索引值对应的值和取链表的值,效率几乎一样;
- LocateElem(L,e,compare()); 和链表中的元素做对比
- InsertElem(&L,i,e); 在链表中插入元素,复杂度O(1),在提供位置的时候,若不提供位置,还需要加搜索的时间
- DeleteElem(&L,i,&e); 在链表中删除元素,复杂度O(1)
 ……
}
eg:  
| 1 | typedef struct LNode { | 
 
    
链表的两种头部:
- 没有头的链表:第一个块就开始存储数据
- 任何时候都有头的链表: 第一个块不存储数据,作为头节点,第二个块开始存数据
- 应用:在leetcode中的ReverseLinklist中,当反转的是从1开始的时候,若使用的是没有头的,则需要额外判断处理,否则可以统一处理;
 
  
链表的几个常见操作:
- 取第i个元素:  1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12Status GetElem_L(LinkList L, int i, ElemType &e) 
 {//查找操作
 p = L->next;
 j = 1;
 while( p && j < i){
 p = p->next;
 ++j;
 }
 if (!p || j>i) return ERROR;
 e = p->data;
 return OK;
 }
- 插入元素:在第i个位置上插入      1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11Status ListInsert_L(LinkList &L, int i, ElemType e) 
 {
 p = L; j = 0;
 while (p && j < i-1) { p = p->next; ++j }
 if (!p || j>i-1) return ERROR;
 s = (LinkList) malloc( sizeof (LNode) );
 s->data = e;
 s->next = p->next;
 p->next = s;
 return OK;
 }
- 删除元素:删除第i个元素:    1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11Status ListDelete_L(LinkList &L, int i, ElemType &e) 
 {
 p = L; j = 0;
 while (p->next && j < i-1) { p = p->next; ++j }
 if (!(p->next) || j>i-1) return ERROR;
 q = p->next;
 e = q->data;
 p->next = p->next->next; //(p->next = q->next;)
 free(q);
 return OK;
 }
链表的建立:
- 头插法:    1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24CreateList_L(LinkList &L, int n) 
 {
 L = (LinkList) malloc( sizeof (LNode) );
 L->next = NULL;
 for( i=n; i>0; --i){
 s = (LinkList) malloc( sizeof (LNode) );
 scanf( &s->data);
 s->next = L->next; ①
 L->next = s; ②
 }
 }```
 + 尾插法:
 {% asset_img tailbuile.png 尾插法示意图 %}
 ```c
 CreateList_L(LinkList &L, int n)
 {
 tail = L = (LinkList) malloc( sizeof (LNode) );
 L->next = NULL;
 for( i=n; i>0; --i){
 s = (LinkList) malloc( sizeof (LNode) );
 scanf( &s->data);
 tail->next = s; ①
 tail = s; ②
 } }
链表的常见复杂操作:
- 两个有序链表的合并:1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc) 
 {
 pa = La->next; pb = Lb->next;
 Lc = pc = La;
 while( pa && pb ){
 if(pa->data <= pb->data){
 pc->next = pa; pc = pa; pa = pa->next;
 }
 else{
 pc->next = pb; pc = pb; pb= pb->next;
 }
 }
 pc->next = pa ? pa : pb;
 free( Lb );
 }
一些特殊的链表:
- 单向循环链表:
- 图示:     
- 多重循环链表:
- 双向链表:双向循环链表:1 
 2
 3
 4
 5typedef struct DuLNode{ 
 ElemType data;
 struct DuLNode *prior;
 struct DuLNode *next;
 }DuLNode, *DuLinkList;
探讨:
- 链表作为最基础的数据结构,是对其他数据结构:如栈,树等的支撑,其他这些大部分是在链表的基础上建立的;
- 从较底层的层面:即深入到内存,链表实现了对零散内存的有效使用,内存在为链表分配空间的时候不用大段的连续空间;
- 从较高层,即抽象的层面看,链表仅仅是一批数据的拓扑结构之一,就常见的栈,队列,树,图等等,你还可以想出其他可能有用没用的,如,立体的结构,方块,金字塔形(emm这个像堆),还有随意的,感觉想到了矩阵里面的压缩。
应用:
- 链表的应用:如
- 在文件中,对大文件的存储,采用类似链表的结构,
- 大数相加,像两个100位的整数相加,使用链表,每个块为一位,就可以解决这个问题,数组也行
- 倒排索引结构,在操作系统中用到,我的github上放着一个本科毕业设计做的简单搜索引擎,几乎全是自己搞的(解析网页那块自己c写了状态机,觉得效果不如beatifulsoup好,后面用它了),不过想想那会还不会很有意识的去想如果让它运行在大量用户下,各种使用条件(如输入条件下,会怎么样,年轻啊~)
- 其他,当然是其他数据结构基于链表做的,多了去了