尽管顺序表有存储密度高,可以随机存取结点等优点,但是假如要对顺序表进行插入或删除等一些操作时,经常需要移动诸多结点,如果要频繁的进行插入删除等一些操作,效率十分低下,而且顺序表大小不好确定。这时,用单链表实现其功能就比较方便,而且效率也比较高了。
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素 +指针,元素就是存储数据的存储单元,指针就是连接每个结点的地址数据,即就是每一个对象里边都存有下一个对象的地址。
由于单链表的这种特性,我们只需要定义一个头指针,用其进行维护,使其指向第一个元素的位置,我们便可 访问表中其他元素进行操作。示意图如下:
1.单链表的创建:
由于链表中的每一个对象至少包含一个数据域和一个指向下一个结点的指针。因此,我们可以用结构体来定义这个结点:
typedef struct Node
{
DataType data;
struct Node* next;
}Node,*pNode,*pList;
定义好之后,可创建一个具有指向下一结点的指针,即单链表创建完成。
pNode plist ;
由于单链表由一个头指针进行维护,所以进行初始化时,只需将头指针里存放的地址改为空即可:
要在定义的单链表里插入元素(结点),必须先先在内存中申请一块空间来存放要插入的元素:
(1.)尾删(从链表末尾开始删,一次删除一个元素)
(2.)头删(从链表首部开始删,一次删除一个元素)
5.遍历单链表:
6.单链表的逆置:
思路:依次断开单链表结点,将头指针逐次向后移动,每移动一次,将上次断开对象的的地址放入它的next中,直到为空。
源程序:
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int DataType;
typedef struct ListNode
{
DataType _data;
struct ListNode* _next;
}ListNode;
ListNode* BuyNode(DataType x)
{
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
node->_data = x;
node->_next = NULL;
return node;
}
void PushBack(ListNode** ppList, DataType x)
{
assert(ppList);
ListNode* cur = *ppList;
ListNode* node = BuyNode(x);
if (*ppList == NULL) //空链表
{
*ppList = node;
}
else
{
while (cur->_next)
{
cur = cur->_next;
}
cur->_next = node;
}
}
void PopBack(ListNode** ppList)
{
assert(ppList);
ListNode* cur = *ppList;
ListNode* del = NULL;
if ((*ppList) == NULL)
{
return;
}
else if (cur->_next == NULL) //一个元素
{
free(cur);
cur = NULL;
return;
}
else //
{
while (cur->_next)
{
del = cur;
cur = cur->_next;
}
free(del->_next);
del->_next = NULL;
return;
}
}
void PushFront(ListNode** ppList, DataType x)
{
ListNode* node = BuyNode(x);
ListNode* cur = *ppList;
ListNode* tmp = NULL;
if (*ppList == NULL)
{
*ppList = node;
return;
}
else //一个或多个元素
{
tmp = *ppList;
*ppList = node;
node->_next = tmp;
}
}
void PopFront(ListNode** ppList)
{
assert(ppList);
if (*ppList == NULL)
{
return;
}
else
{
ListNode* tmp = (*ppList)->_next;
free(*ppList);
*ppList = tmp;
}
}
ListNode* Find(ListNode* pList, DataType x)
{
ListNode* cur = pList;
while (cur)
{
if (cur->_data == x)
{
return cur;
}
cur = cur->_next;
}
return NULL;
}
// 在pos的前面插入一个节点x
void Insert(ListNode** ppList, ListNode* pos, DataType x)
{
assert(ppList);
ListNode* pre = NULL;
ListNode* cur = *ppList;
ListNode* tmp= NULL;
if ((pos == NULL) || (*ppList == NULL))
{
PushBack(ppList, x);
}
if (*ppList == pos) //给第一个位置
{
PushFront(ppList, x);
}
else
{
while (cur)
{
if (cur->_next == pos)
{
pre = cur;
break;
}
cur = cur->_next;
}
tmp = BuyNode(x);
pre->_next = tmp;
tmp->_next = pos;
}
}
void Erase(ListNode** ppList, ListNode* pos)
{
assert(ppList);
ListNode* cur = *ppList;
ListNode* pre = NULL;
ListNode* next = NULL;
if (pos == NULL)
{
return;
}
if (pos == cur) //删除第一个位置
{
PopFront(ppList);
}
else
{
while (cur)
{
if (cur->_next == pos)
{
pre = cur;
break;
}
cur = cur->_next;
}
next = pre->_next->_next;
free(pre->_next);
pre->_next = next;
}
}
void PrintList(ListNode* pList) //不需要改变链表内容,用一级指针
{
if (pList == NULL)
{
printf("链表为空\n");
}
else
{
while (pList)
{
printf("%d ", pList->_data);
pList = pList->_next;
}
printf("\n");
}
}
void TestList()
{
ListNode* a = NULL;
/*
PrintList(a);
PushBack(&a, 1);
PushBack(&a, 2);
PushBack(&a, 3);
PushBack(&a, 4);
PushBack(&a, 5);
PushBack(&a, 6);
PrintList(a);
PopBack(&a);
PrintList(a);
PopBack(&a);
PrintList(a);
PopBack(&a);
PrintList(a);*/
PushFront(&a, 1);
PushFront(&a, 2);
PushFront(&a, 3);
PushFront(&a, 4);
PushFront(&a, 5);
PushFront(&a, 6);
Erase(&a, Find(a, 6));
PrintList(a);
Insert(&a, Find(a, 6), 10);
Insert(&a, Find(a, 4), 10);
Find(a, 3);
PrintList(a);
PopFront(&a);
PopFront(&a);
PopFront(&a);
PopFront(&a);
PopFront(&a);
PopFront(&a);
PopFront(&a);
PopFront(&a);
PrintList(a);
}