链表(线性表的一种存储结构)

链表

  • 一、单链表
    • 1.代码定义单链表
    • 2.两种实现
      • 带头结点
      • 不带头结点
    • 3.单链表的操作
      • 插入
        • 按位序插入
        • 指定结点的后插操作
        • 指定结点的前插操作
      • 删除
        • 按位序删除
        • 指定结点的删除
      • 查找
        • 按位查找
        • 按值查找
    • 4.单链表的建立
      • 尾插法建立单链表
      • 头插法

链表分为

  • 单链表
  • 双链表
  • 循环链表
  • 静态链表

一、单链表

顺序表(顺序存储):
每个节点中只存放数据元素。优点:可随机存取,存储密度高;缺点:要求大片连续空间,改变容量较为麻烦。
单链表(链式存储):
每个结点除了存放数据元素外,还要存储指向下一个节点的指针。优点:不要求大量连续空间,可以很方便地拓展容量;缺点:不可随机存取,要耗费一定空间存放指针。
链表(线性表的一种存储结构)_第1张图片

1.代码定义单链表

struct LNode{           //定义单链表结点类型
    ElemType data;      //每个结点存放一个数据元素;
    struct LNode *next; //指针指向下一个节点
}

//增加一个新的节点:在内存中申请一个节点所需空间,并用指针p指向这个节点
struct LNode * p = (struct LNode *) malloc(sizeof(struct LNode));

typedef关键字——数据类型重命名

//使用该格式对数据类型进行重命名,方便使用
typedef <数据类型> <别名>

typedef struct LNode LNode; //将struct LNode重命名为了LNode
//在后续使用时不再需要带struct字段,可直接写为:
LNode * p = (LNode *) malloc(sizeof(LNode));

有比以上更简洁的重命名方式,可在定义单链表结构体时直接改变

typedef struct LNode {
	ElemType data;
	struct LNode *next;
}LNode, *LinkList;

//上方写法相当于
struct LNode{           
    ElemType data;      
    struct LNode *next; 
};
typedef struct LNode LNode;  //强调这是一个结点
typedef struct LNode *LinkList;  //强调这是一个单链表

强调这是一个结点 ——使用LinkList
强调这是一个单链表 ——使用LNode

要表示一个单链表时,只需声明一个头指针L,指向单链表的第一个结点。

2.两种实现

不带头结点,写代码较为麻烦。对第一个数据结点和后续数据结点的处理需要不同的代码逻辑;对空表和非空表的处理需要用不用的代码逻辑

带头结点

初始化一个带头结点的单链表
链表(线性表的一种存储结构)_第2张图片

bool InitList(LinkList &L) {
	L = (LNode *) malloc(sizeof(LNode));	//分配一个头结点
	if(L == NULL)	//内存不足,分配失败
		return false;
	L->next = NULL;	//头结点之后暂时还没有结点
	return true;
}
void test() {
	LinkList L;		//声明一个指向单链表的指针,此处并没有创建一个结点
	InitList(L);
}

//判断单链表是否为空
bool Empty(LinkList L) {
	if(L->next == NULL)
		return true;
	else
		return false;
}

不带头结点

初始化一个不带头结点的单链表
链表(线性表的一种存储结构)_第3张图片

bool InitList(LinkList &L) {
	L = NULL;	//空表,暂时还没有任何结点,防止脏数据
	return true;
}
void test() {
	LinkList L;		//声明一个指向单链表的指针,此处并没有创建一个结点
	InitList(L);
}

//判断单链表是否为空
bool Empty(LinkList L) {
	if(L == NULL)
		return true;
	else
		return false;
}

3.单链表的操作

插入

按位序插入

ListInsert(&L, i, e): 在表L中的第i个位置上插入指定元素e。

方法1带头结点的单链表):

  • 找到第i-1个结点
  • 使用malloc申请一个新的节点,将e放入该节点
  • 对指针进行修改,将新结点插入其后
    链表(线性表的一种存储结构)_第4张图片
    链表(线性表的一种存储结构)_第5张图片
bool ListInsert(LinkList &L, int i, ElemType e) {
    if(i<1)
        return false;
    LNode *p;   //指针p指向当前扫描到的结点
    int j = 0;  //当前p指向的是第几个结点
    p = L;      //L指向头结点,头结点是第0个结点(不存数据)
    while(p != NULL && j<i-1) { //循环找到第i-1个结点
        p = p->next;
        j++;
    }
    if(p == NULL)   //i值不合法
        return false;
    
    LNode *s = (LNode *)malloc(sizeof(LNode));
    s->data = e;
    s->next = p->next;
    p->next = s;    //将结点s连到p之后
    return true;    //插入成功
}

时间复杂度分析:

  1. 若i=1(插在表头) 最好时间复杂度: O ( 1 ) O(1) O(1)
  2. 若i=n(插在表尾) 最坏时间复杂度: O ( n ) O(n) O(n)
  3. 平均时间复杂度: O ( n ) O(n) O(n)

方法2不带头结点的单链表):
方法与带头结点基本相同。
由于不存在“第0个”结点(头结点),因此i=1时需要特殊处理:

  • malloc申请新的节点,将元素e放入
  • 让新结点的next指针指向L(头指针)指向的节点
  • 最后让L(头指针)指向新结点
    链表(线性表的一种存储结构)_第6张图片

需要在之前的ListInsert函数中加入一段对于插入头部情况处理的代码:

if(i == 1) { //插入第1个节点的操作与其他节点操作不同
	LNode *s = (LNode *)malloc(sizeof(LNode));
	s->data = e;
	s->next = L;
	L = s;	//头指针指向新结点
	return true;
}
指定结点的后插操作

某些情况下可能分配失败(内存不足)
链表(线性表的一种存储结构)_第7张图片

指定结点的前插操作

链表(线性表的一种存储结构)_第8张图片
时间复杂度: O ( 1 ) O(1) O(1)

删除

按位序删除

ListDelete(&L, i, &e):删除表L中的第i个位置的元素,并用e返回删除元素的值。
方法1(带头结点):

  • 找到第i-1个节点
  • 将其指针指向第i+1个节点
  • 释放第i个节点
    链表(线性表的一种存储结构)_第9张图片
    时间复杂度:
    最坏、平均时间复杂度: O ( n ) O(n) O(n)
    最好时间复杂度: O ( 1 ) O(1) O(1)
指定结点的删除

查找

按位查找

GetElem(L,i):获取表L中第i个位置的元素的值。

LNode * GetElem(LinkList L, int i) {
    if(i<0) 
        return NULL;
    LNode *p;	//指针p指向当前扫描的节点
    int j = 0;	//当前p指向的是第几个节点
    p = L;		//L指向头结点,头结点是第0个节点(不存数据)
    while(p!=NULL && j<i) {	//循环找到第i个节点
        p = p->next;
        j++;
    }
    return p;
}
按值查找

LocateElem(L,e):在表L中查找具有给定关键字值的元素

LNode * LocateElem(LinkList L, ElemType e) {
    LNode *p = L->next;
    //从第1个节点开始查找数据域为e的节点
    while(p != NULL && p->data != e)
        p = p->next;
    return p;
    //找到后返回该节点指针,否则返回NULL
}

时间复杂度: O ( 1 ) O(1) O(1)

4.单链表的建立

如果给你很多个数据元素,要把它们存到一个单链表里边,如何做?
Step 1:初始化一个单链表
Step 2:每次取一个数据元素,插入到表尾/表头(分别对应尾插法头插法

尾插法建立单链表

Step 1:初始化单链表
Step 2:设置变量length记录链表长度
Step 3: While 循环{
每次取一个数据元素e;
ListInsert(L, length+1, e) 插到尾部;
length++;
}

ListInsert函数中的while循环,每次都从头开始之后遍历,时间复杂度为 O ( n 2 ) O(n^2) O(n2)
可使用后插操作InsertNextNode进行时间复杂度简化
链表(线性表的一种存储结构)_第10张图片

头插法

对头结点的后插操作
Step 1:初始化单链表
Step 2:While 循环{
每次取一个数据元素e;
InsertNextNode(L, length+1, e) 插到尾部;
}

你可能感兴趣的:(数据结构和算法,数据结构,链表,散列表)