实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
- 单向、双向
- 带头、不带头
- 循环、非循环
单链表、双向链表
不带头单链表、带头链表
循环单链表、非循环单链表
无头单向非循环链表
1.OJ题
2.基本不会单独作为链表使用
3.使用其他数据结构的一部分:图、哈希表
带头双向循环链表
1.作为链表使用
STL中list就是这种结构
定义结点结构体
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* prev;
struct ListNode* next;
LTDataType data;
}ListNode;
遍历打印链表
//遍历打印链表
void ListPrint(ListNode* phead)
{
ListNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
初始化
//初始化
void ListInit(ListNode** phead)
{
*phead = BuyListNode(0);
(*phead)->next = *phead;
(*phead)->prev = *phead;
}
创建一个新结点
//创建一个新结点
ListNode* BuyListNode(LTDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
perror("malloc fail!");
exit(1);
}
newnode->prev = NULL;
newnode->next = NULL;
newnode->data = x;
return newnode;
}
尾插
//尾插
void ListPushBack(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* newnode = BuyListNode(x);
ListNode* tail = phead->prev;
tail->next = newnode;
newnode->prev = tail;
phead->prev = newnode;
newnode->next = phead;
}
尾删
//尾删
void ListPopBack(ListNode* phead)
{
assert(phead);
assert(phead != phead->next);
ListNode* tail = phead->prev;
ListNode* tailprev = tail->prev;
tailprev->next = phead;
phead->prev = tailprev;
free(tail);
}
首插
//首插
void ListPushFront(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* newnode = BuyListNode(x);
ListNode* first = phead->next;
phead->next = newnode;
newnode->prev = phead;
first->prev = newnode;
newnode->next = first;
}
首删
//首删
void ListPopFront(ListNode* phead)
{
assert(phead);
assert(phead != phead->next);
ListNode* first = phead->next;
ListNode* second = first->next;
phead->next = second;
second->prev = phead;
free(first);
}
查找
//查找
ListNode* ListFind(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
pos的前一个位置插入
//pos的前一个位置插入
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* newnode = BuyListNode(x);
ListNode* posprev = pos->prev;
posprev->next = newnode;
newnode->prev = posprev;
newnode->next = pos;
pos->prev = newnode;
}
删除指定位置pos
//删除指定位置pos
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* posprev = pos->prev;
ListNode* posnext = pos->next;
posprev->next = posnext;
posnext->prev = posprev;
free(pos);
}
总代码
List.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* prev;
struct ListNode* next;
LTDataType data;
}ListNode;
//遍历打印链表
void ListPrint(ListNode* phead);
//初始化
void ListInit(ListNode** phead);
//创建一个新结点
ListNode* BuyListNode(LTDataType x);
//尾插
void ListPushBack(ListNode* phead, LTDataType x);
//尾删
void ListPopBack(ListNode* phead);
//首插
void ListPushFront(ListNode* phead, LTDataType x);
//首删
void ListPopFront(ListNode* phead);
//查找
ListNode* ListFind(ListNode* phead, LTDataType x);
//pos的前一个位置插入
void ListInsert(ListNode* pos, LTDataType x);
//删除pos位置
void ListErase(ListNode* pos);
List.c
#include "List.h"
//遍历打印链表
void ListPrint(ListNode* phead)
{
ListNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
//初始化
void ListInit(ListNode** phead)
{
*phead = BuyListNode(0);
(*phead)->next = *phead;
(*phead)->prev = *phead;
}
//创建一个新结点
ListNode* BuyListNode(LTDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
perror("malloc fail!");
exit(1);
}
newnode->prev = NULL;
newnode->next = NULL;
newnode->data = x;
return newnode;
}
//尾插
void ListPushBack(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* newnode = BuyListNode(x);
ListNode* tail = phead->prev;
tail->next = newnode;
newnode->prev = tail;
phead->prev = newnode;
newnode->next = phead;
}
//尾删
void ListPopBack(ListNode* phead)
{
assert(phead);
assert(phead != phead->next);
ListNode* tail = phead->prev;
ListNode* tailprev = tail->prev;
tailprev->next = phead;
phead->prev = tailprev;
free(tail);
}
//首插
void ListPushFront(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* newnode = BuyListNode(x);
ListNode* first = phead->next;
phead->next = newnode;
newnode->prev = phead;
first->prev = newnode;
newnode->next = first;
}
//首删
void ListPopFront(ListNode* phead)
{
assert(phead);
assert(phead != phead->next);
ListNode* first = phead->next;
ListNode* second = first->next;
phead->next = second;
second->prev = phead;
free(first);
}
//查找
ListNode* ListFind(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
//pos的前一个位置插入
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* newnode = BuyListNode(x);
ListNode* posprev = pos->prev;
posprev->next = newnode;
newnode->prev = posprev;
newnode->next = pos;
pos->prev = newnode;
}
//删除指定位置pos
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* posprev = pos->prev;
ListNode* posnext = pos->next;
posprev->next = posnext;
posnext->prev = posprev;
free(pos);
}
test.c
#include "List.h"
int main()
{
ListNode* phead = NULL;
ListInit(&phead);
//测试尾插
ListPushBack(phead, 1);
ListPushBack(phead, 2);
ListPushBack(phead, 3);
ListPrint(phead);
//测试尾删
ListPopBack(phead);
ListPrint(phead);
//测试首插
ListPushFront(phead, 4);
ListPushFront(phead, 5);
ListPushFront(phead, 6);
ListPrint(phead);
//测试首删
ListPopFront(phead);
ListPrint(phead);
//测试pos的前一个位置插入
ListNode* pos = ListFind(phead,1);
ListInsert(pos, 9);
ListPrint(phead);
//测试删除pos位置
pos = ListFind(phead, 1);
ListErase(pos);
ListPrint(phead);
}
链表和顺序表(数组)的区别和联系:
- 顺序表就是在数组的基础上实现怎删改查,并且插入时可以动态增长。
顺序表缺陷:
a.可能存在一定空间浪费
b.增容有一些效率损失
c.中间或者头部插入删除,时间复杂度为O(N),因为要挪动数据
这些问题链表解决。
链表的缺陷:不能随机访问
互补的数据结构