数据结构《二》链表的实现
Posted AURORA_CODE
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构《二》链表的实现相关的知识,希望对你有一定的参考价值。
链表的实现
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
一、单向无头链表的实现
1.功能实现
01 struct SListNode
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
02 动态申请新结点
SListNode* BuySListNode(SLTDateType x)
{
SListNode *node = (SListNode*)malloc(sizeof(SListNode));
if (NULL == node)
{
printf("BUYSListNode Error!");
return NULL;
}
node->data = x;
node->next = NULL;
return node;
}
03 无头单向链表的打印
void SListPrint(SListNode* plist)
{
SListNode *current = plist;
while (current!=NULL)
{
printf("%d --> ", current->data);
current = current->next;
}
printf("NULL\\n");
}
04 无头单向链表的销毁
void SListDestory(SListNode** pplist)
{
SListNode *node = NULL;
if (NULL == *pplist)
{
return;
}
while (NULL!=(*pplist))
{
node = *pplist;
(*pplist) = (*pplist)->next;
free(node);
}
}
05 无头单向链表的头插头删
void SListPushFront(SListNode** pplist, SLTDateType x)
{
assert(pplist != NULL);
SListNode *node = BuySListNode(x);
node->next = *pplist;
*pplist=node;
}
void SListPopFront(SListNode** pplist)
{
SListNode *current = NULL;
assert(pplist != NULL);
if (NULL==*pplist)
{
return;
}
else
{
current = *pplist;
*pplist = (*pplist)->next;
free(current);
}
}
06 无头单向链表的尾插尾删
void SListPushBack(SListNode** pplist, SLTDateType x)
{
assert(pplist != NULL);
SListNode *current = *pplist;
if (*pplist==NULL)
{
*pplist = BuySListNode(x);
}
else
{
while (current->next)
{
current = current->next;
}
current->next = BuySListNode(x);
}
}
void SListPopBack(SListNode** pplist)
{
assert(pplist != NULL);
SListNode *current = *pplist;
if (*pplist == NULL)
{
return;
}
else if (NULL==(*pplist)->next)
{
free(*pplist);
*pplist = NULL;
}
else
{
while (current->next->next)
{
current = current->next;
}
free(current->next);
current->next = NULL;
}
}
07 无头单向链表的查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
SListNode *current = plist;
while (current)
{
if (x==current->data)
{
return current;
}
current = current->next;
}
return NULL;
}
08 无头单向链表的按位插入
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
SListNode *node = NULL;
if (NULL == pos)
{
printf("POS ERROR\\n");
return;
}
node = BuySListNode(x);
node->next = pos->next;
pos->next = node;
}
09 无头单向链表的按位删除
// 单链表在pos位置之后删除
void SListEraseAfter(SListNode* pos)
{
SListNode *node = NULL;
if (NULL == pos)
{
printf("POS ERROR\\n");
return;
}
node = pos->next;
pos->next = node->next;
free(node);
}
2. 测试代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include"slist.h"
void test_Back()
{
SListNode *head = NULL;
printf("尾插测试\\n");
SListPushBack(&head, 1);
SListPrint(head);
SListPushBack(&head, 2);
SListPrint(head);
SListPushBack(&head, 3);
SListPrint(head);
SListPushBack(&head, 4);
SListPrint(head);
SListPushBack(&head, 5);
SListPrint(head);
printf("尾删测试\\n");
SListPopBack(&head);
SListPrint(head);
SListPopBack(&head);
SListPrint(head);
SListPopBack(&head);
SListPrint(head);
SListPopBack(&head);
SListPrint(head);
SListPopBack(&head);
SListPrint(head);
SListDestory(&head);
}
void test_Front()
{
SListNode *head = NULL;
printf("头插测试\\n");
SListPushFront(&head, 1);
SListPrint(head);
SListPushFront(&head, 2);
SListPrint(head);
SListPushFront(&head, 3);
SListPrint(head);
SListPushFront(&head, 4);
SListPrint(head);
SListPushFront(&head, 5);
SListPrint(head);
printf("头删测试\\n");
SListPopFront(&head);
SListPrint(head);
SListPopFront(&head);
SListPrint(head);
SListPopFront(&head);
SListPrint(head);
SListPopFront(&head);
SListPrint(head);
SListPopFront(&head);
SListPrint(head);
SListDestory(&head);
}
void test()
{
SListNode *head = NULL;
SListPushFront(&head, 1);
SListPushFront(&head, 2);
SListPushFront(&head, 3);
SListPushFront(&head, 4);
SListPushFront(&head, 5);
SListPushFront(&head, 6);
SListPushFront(&head, 7);
SListPushFront(&head, 8);
SListPushFront(&head, 9);
SListPrint(head);
printf("指定位置之后删除测试:\\n");
SListEraseAfter(SListFind(head, 7));
SListPrint(head);
printf("指定位置之后插入测试:\\n");
SListInsertAfter(SListFind(head, 7), 6);
SListPrint(head);
SListDestory(&head);
}
int main()
{
//test_Back();
//test_Front();
test();
return 0;
}
二、双向带头循环链表的实现
1. 功能实现
01 struct LinkNode
typedef int LTDataType;
typedef struct ListNode
{
LTDataType _data;
struct ListNode* _next;
struct ListNode* _prev;
}ListNode;
02 动态申请链表新结点
ListNode* ListCreate(LTDataType x)
{
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
if (NULL == node)
{
assert(0);
}
node->_data = x;
node->_prev = NULL;
node->_next = NULL;
return node;
}
03 双向带头循环链表的初始化
ListNode* ListInit()
{
ListNode* pHead = ListCreate(0);
pHead->_next = pHead;
pHead->_prev = pHead;
return pHead;
}
04 双向带头循环链表的销毁
void ListDestory(ListNode* pHead)
{
assert(pHead);
while (pHead->_next != pHead)
{
ListErase(pHead->_next);
}
free(pHead);
pHead = NULL;
}
05 双向带头循环链表的打印
void ListPrint(ListNode* pHead)
{
assert(pHead);
ListNode* pcurrent = pHead->_next;
printf("head --> ");
while (pHead != pcurrent)
{
printf("%d --> ", pcurrent->_data);
pcurrent = pcurrent->_next;
}
printf("head\\n");
}
06 双向带头循环链表的查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* pcurrent = pHead->_next;
while (pcurrent != pHead)
{
if (x == pcurrent->_data)
return pcurrent;
pcurrent = pcurrent->_next;
}
return NULL;
}
07 双向带头循环链表的按位插入
void ListInsert(ListNode* pos, LTDataType x)
{
if (NULL == pos)
{
return;
}
ListNode* newnode = ListCreate(x);
newnode->_prev = pos->_prev;
newnode->_next = pos;
pos->_prev->_next = newnode;
pos->_prev = newnode;
}
08 双向带头循环链表的按位删除
void ListErase(ListNode* pos)
{
if (NULL == pos)
{
return;
}
pos->_prev->_next = pos->_next;
pos->_next->_prev = pos->_prev;
free(pos);
pos = NULL;
}
09 双向带头循环链表的头插头删
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListInsert(pHead->_next, x);
}
// 双向链表头删
void ListPopFront(ListNode* pHead)
{
assert(pHead);
ListErase(pHead->_next);
}
10 双向带头循环链表的尾插尾删
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListInsert(pHead, x);
}
// 双向链表尾删
void ListPopBack(ListNode* pHead)
{
assert(pHead);
ListErase(pHead->_prev);
}
2. 测试代码
#include"linklist.h"
int main()
{
ListNode *head = ListInit();
//先头插5个元素1,2,3,4,5
printf("先将1,2,3,4,5头插入链表得:\\n");
ListPushFront(head, 1);
ListPushFront(head, 2);
ListPushFront(head, 3);
ListPushFront(head, 4);
ListPushFront(head, 5);
ListPrint(head);
printf("再将6,7,8尾插入链表得:\\n");
//再尾插3个元素6,7,8
ListPushBack(head, 6);
ListPushBack(head, 7);
ListPushBack(head, 8);
ListPrint(head);
//头删1个结点
printf("头删一个节点得:\\n");
ListPopFront(head);
ListPrint(head);
printf("尾删一个节点得:\\n");
ListPopBack(head);
ListPrint(head);
printf("找出值等于2的节点并删除:\\n");
ListErase(ListFind(head, 2));
ListPrint(head);
printf("在值等于1的元素前插入888:\\n");
ListInsert(ListFind(head, 1), 888);
ListPrint(head);
ListDestory(head);
}
以上是我对一些比较常见的链表的实现。如有错误,希望大佬指正。
同时,经过对顺序表和链表的学习,我对链表和顺序表的异同以及优缺点做了总结,在此分享出来一同探讨。
角度 | 顺序表 | 链表 |
---|---|---|
存储 | 存储空间连续 | 存储空间不一定连续 |
随机插入/删除 | 搬移元素 O(n) | 不需要搬移元素,直接修改指针指向 O(1) |
随机访问 | 支持 O(1) | 不支持 O(n) |
获取下一个元素的方式 | 对下标 + +/- - | next/prev |
插入 | 需要考虑容量,是否需要扩容 | 不需要考虑容量 |
空间 | 扩容时才需要再次申请空间 | 频繁向堆申请小的内存块 |
应用场景 | 元素高效存储+频繁访问 | 任意位置频繁地插入删除 |
缓存利用率 | 高 | 低 |
以上是关于数据结构《二》链表的实现的主要内容,如果未能解决你的问题,请参考以下文章