链表的概念及结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
- 单向、双向
- 带头、不带头
- 循环、非循环
一. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结
构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
二. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向
循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。
头指针指向头节点,头插就是把新插入的这个数据变成头
知道了头指针,就能知道从什么位置开始访问,头指针必须存在
单链表的实现:
#include<stdio.h>
#include<stdlib.h>
typedef int LDataType;
//定义节点:数据+指针
typedef struct listNode
{
LDataType _data;
struct listNode* _next;
}listNode;
//listNode 是 struct listNode 的别名
//链表
typedef struct list
{
//保存第一个节点的地址
listNode* _head;
}list;
listNode* creatListNode(LDataType val)//返回一个指针
{
listNode* node = (listNode*)malloc(sizeof(listNode));
node->_data = val;
node->_next = NULL;
return node;
}
void listInit(list* lst)//传入的是指针,故&
{
//初始化为空链表
if (lst == NULL)
return;
lst->_head = NULL;
}
void listPushBack(list*lst, LDataType val)//尾插:时间复杂度:O(n)
{
if (lst == NULL)
return;
if (lst->_head = NULL)//第一个节点为空
{
//插入第一个节点
//创建节点
lst->_head=creatListNode(val);//头节点
}
else
{
//从前向后遍历到最后一个节点
struct listNode* tail = lst->_head;
while (tail->_next != NULL)
{
tail = tail->_next;
}
tail->_next = creatListNode(val);
}
}
void listPopBack(list* lst)//尾删:O(n)
{
//尾删的prev在头指针,tail在头节点 -----important
if (lst == NULL || lst->_head == NULL)
return;
//遍历,找到最后一个节点
struct listNode* prev = NULL;
struct listNode* tail = lst->_head;
while (tail->_next != NULL)
{
prev = tail;
tail = tail->_next;
}
//释放最后一个节点
free(tail);
//更新next
if (prev == NULL)//只有一个节点,更新head
//if(lst->_head->_next==NULL)
lst->_head = NULL;//头空
else
prev->_next = NULL;//指向为空
}
//头插:O(1)
void listPushFront(list* lst, LDataType val)
{
if (lst == NULL)
return;
//创建节点
struct listNode* node = creatListNode(val);
//node head
node->_next = lst->_head;
lst->_head = node;
}
//头删:O(1)
void listPopFront(list* lst)
{
if (lst == NULL || lst->_head == NULL)
return;
struct listNode* next = lst->_head->_next;
free(lst->_head);
lst->_head = next;
}
void listInsertAfter(listNode* node, LDataType val)
{
//给一个节点的后边插入
if (node == NULL)
return;
struct listNode* newNode = creatListNode(val);
//node newNode next
struct listNode* next = node->_next;
node->_next = newNode;
newNode->_next = next;
}
void listEraseAfter(listNode* node)
{
//删除node的下一个位置
if (node == NULL || node->_next == NULL)
return;
struct listNode* next = node->_next;
struct listNode* nextnext = next->_next;
//释放next
free(next);
node->_next = nextnext;
}
struct listNode* listFind(list* lst, LDataType val)
{
if (lst == NULL || lst->_head == NULL)
return NULL;
struct listNode* cur = lst->_head;
while (cur)
{
if (cur->_data == val)
return cur;
cur = cur->_next;
}
return NULL;
}
void listDestroy(list* lst)//传了链表的一个指针
{
//释放当前节点保存下一个的值
if (lst == NULL)
return NULL;
//头空不空已经判断
struct listNode* cur = lst->_head;
while (cur)
{
struct listNode* next = cur->_next;
free(cur);//释放当前
cur = next;//更新到下一个节点的位置
}
lst->_head = NULL;
}
void test()
{
list lst;
listInit(&lst);
}
int main()
{
test();
return 0;
}
顺序表和链表的区别和联系
顺序表:
空间连续,支持连续访问
中间或前面部分的插入删除时间复杂度为O(n);并且增容的代价比较大
链表:
以节点为字节存储,不支持随机访问
任意位置插入删除时间复杂度都为O(1);插入一个开辟一个空间
带头双向循环链表
#include<stdio.h>
#include<stdlib.h>
typedef int LDataType;
//双向节点
typedef struct ListNode
{
LDataType _data;
struct ListNode* _prev;
struct ListNode* _next;
}ListNode;
//双向带头循环链表
typedef struct List
{
struct ListNode* _head;
}List;
void initList(List* lst)
{
//空的带头循环双向链表
//构建循环结构
//创建头节点
lst->_head = (struct ListNode*)malloc(sizeof(struct ListNode));
//循环
lst->_head->_prev = lst->_head->_next = lst->_head;
}
struct ListNode* creatNode(LDataType val)
{
struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));
node->_data = val;
node->_next = node->_prev = NULL;
}
void listPushBack(List* lst, LDataType val)//尾插
{
struct ListNode* tail = lst->_head->_prev;
struct ListNode* newNode = creatNode(val);
//head ......tail newNode
tail->_next = newNode;
newNode->_prev = tail;
lst->_head->_prev = newNode;
newNode->_next = lst->_head;
}
//空链表不能删除,会破坏双向带头循环的结构
void listPopBack(List* lst)//尾删:O(1)
{
if (lst->_head->_prev == lst->_head)
return;//空链表
struct ListNode* tail = lst->_head->_prev;
struct ListNode* prev = tail->_prev;
free(tail);
prev->_next = lst->_head;
lst->_head->_prev = prev;
}
void listPushFront(List* lst, LDataType val)//头插
{
struct ListNode* newNode = creatNode(val);
struct ListNode* next = lst->_head->_next;
lst->_head->_next = newNode;
newNode->_prev= lst->_head;
next->_prev = newNode;
newNode->_next = next;
}
void listPopFront(List* lst)//头删
{
if (lst->_head->_next == lst->_head)
return;
struct ListNode* next = lst->_head->_next;
struct ListNode* nextnext = next->_next;
free(next);
lst->_head->_next = nextnext;
nextnext->_prev = lst->_head;
//listErase(lst->_head->_next);
}
void listInsert(ListNode* node, LDataType val)
{
//从node前插入一个节点
struct ListNode* newNode = creatNode(val);
struct ListNode* prev = node->_prev;
// prev newNode node
node->_prev = newNode;
newNode->_next = node;
prev->_next = newNode;
newNode->_prev = prev;
}
//头插:insert(head->next,val)
//尾插:insert(head,val)
void listErase(ListNode* node)
{
struct ListNode* prev = node->_prev;
struct ListNode* next = node->_next;
free(node);
prev->_next = next;
next->_prev = prev;
}
listDestroy(List* lst)
{
struct ListNode* cur = lst->_head->_next;
while (cur != lst->_head)
{
struct ListNode* next = cur->_next;
free(cur);
cur = next;
}
free(lst->_head);
lst->_head = NULL;
}
void printList(List* lst)
{
struct ListNode* cur = lst->_head->_next;//第一个值
while (cur!=lst->_head)
{
printf("%d ", cur->_data);
cur = cur->_next;
}
printf("\n");
}
void test()
{
struct List lst;
initList(&lst);
}
int main()
{
test();
return 0;
}