Skip to content

Latest commit

 

History

History
94 lines (60 loc) · 4.33 KB

linkedlist.md

File metadata and controls

94 lines (60 loc) · 4.33 KB

LinkedList

线性表的顺序存储结构,是逻辑关系上相邻的两个元素在物理位置上也相邻,因此可以随机存取表中任一元素,但是它的缺点就是删除、插入时,需要移动大量元素。

线性表的链式存储结构,由于它不要求逻辑上相邻的元素在物理位置上也相邻,因此它没有顺序存储结构所具有的弱点,但也失去了顺序存储可随机存取的优点。

链表分为单链表、双链表、循环链表、静态链表。

链表是通过一组任意的存储单元来存储线性表中的数据元素,并且还存放一个指向其后继结点的指针,双链表还会存放指向前驱结点的指针。

单链表

单链表中结点类型的描述如下:

class LNode {
  data: any;    //数据域
  next: LNode;  //指针域
}

单链表的元素是离散地分布在存储空间中的,所以单链表是非随机存取的存储结构,既不能直接找到表中某个特定的结点。查找某个特定的结点时,需要从表头开始遍历依次查找。

通常,我们会加多一个 头结点 来标识一个链表。头结点的数据域可以不存储任何信息,也可以记录表长等信息。头结点的指针指向线性表的第一个结点,但头指针的指针域为 NULL 时,表示次单链表是空的。

引入头结点后,可以带来两个优点:

  1. 由于开始结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作和在表的其他位置上的操作一致,无须进行特殊处理。
  2. 无论链表是否为空,空表中头结点的指针域为空,因此空表和非空表的处理就统一了。

单链表的建立可以采用 头插法尾插法,其插入的时间复杂度是 O(1)。

查找结点操作的时间复杂度是 O(n)。

插入结点操作的时间复杂度是 O(1),如果是在某个结点前插入一个新结点,可以转换成在某结点后插入,再交换两个元素。

删除结点操作的时间复杂度是 O(n),因为找到其前驱结点需要 O(n) 的时间。

双链表

单链表结点中只有一个指向其后继的指针,这使得单链表只能从头结点依次顺序地向后遍历。 若要访问某个结点的前驱结点(插入、删除操作时),只能从头开始遍历,访问后继结点的时间复杂度是 O(1),访问前驱结点的时间复杂度是 O(n)。

在双链表中,拥有两个指针域 priornext,分别指向其前驱结点和后继结点。

class DNode{
  data: any;    //数据域
  prior: DNode; //前驱结点域
  next: DNode;  //后继结点域
}

循环链表

循环单链表

循环单链表与单链表的区别在于,表中最后一个结点的指针不是 NULL,而应该指向头结点,从而整个链形成一个环。

在循环单链表中没有指针域为 NULL 的结点,因此循环单链表的判空条件不是头结点的指针是否为空,而是它是否等于头指针。

循环单链表设置 尾指针 会更加高效,因若设置的是头指针,那么对表尾进行操作需要 O(n) 的时间复杂度。而如果设置的是尾指针 r,则 r->next 即为头指针了,对表头与表尾的操作都可以在 O(1) 的时间内完成。

循环双链表

在循环双链表中,头结点的 prior 指针指向表尾结点,表尾结点的 next 指针指向头结点。

循环双链表判断是否为空表时,判断头结点的 prior 和 next 指针域是否指向当前头结点。

静态链表

静态链表是借助数组来描述线性表的链式存储结构,结点有数据域 data 和 指针域 next。这里的指针是结点的相对地址(数组下标),又称为游标。

静态链表是需要预先分配一块连续的内存空间的。在静态链表中可以以 next = -1 作为其结束的标识。

静态链表的插入、删除操作与动态链表相同,只需要修改指针,而不需要移动元素。

static MAX_SIZE = 100;
class StaticLinkList {
  data: any;
  next: number;
}

C语言定义:

#define MaxSize 50  //静态链表的最大长度
typedef struct {    //静态链表结构类型的定义
  ElemType data;    //存储数据元素
  int next;         //下一个元素的数组下标
} SLinkList[MaxSize];