链表
- 链表是链式存储方式实现的线性表
- 逻辑上相邻的数据元素物理上不一定相邻
单链表
- 单链表的节点有一个数据域和一个指针域,数据域存放数据,指针域用来指向下一个节点
单链表基本操作的实现(C语言)
#include <stdio.h>
#include <stdlib.h>
//单链表数据类型
typedef struct LNode{ //节点
int data; //数据域
struct LNode *next; //指针域
}LNode, *LinkList;
/*
typedef的用法,typedef 结构体 别名
typedef struct LNode LNode; //强调单链表的节点
typedef struct LNode *LinkList; //强调单链表本身
*/
//单链表的初始化
/*
void InitList(LinkList L) //不带头节点
{
L = NULL; //空表,无任何节点
}
*/
void InitList(LinkList L) //带头节点
{
L = (LNode *) malloc(sizeof(LNode)); //默认为带头节点的单链表,后续以带头节点的单链表为例
L->next = NULL; //头节点的指针域指向NULL
}
//单链表的创建
LinkList BuildListHead(LinkList L) //头插法
{
int len, x, i;
LNode *s;
L = (LinkList) malloc(sizeof(LNode)); //创建头节点
L->next = NULL; //初始化为空
printf("请输入单链表的长度:");
scanf("%d", &len);
printf("\n");
for(i = 0; i < len; i++)
{
printf("请输入单链表中节点的值:");
scanf("%d", &x);
s = (LNode *) malloc(sizeof(LNode)); //创建新节点
s->data = x;
s->next = L->next;
L->next = s;
printf("\n");
}
return L;
}
LinkList BuildListTail(LinkList L) //尾插法
{
int len, x, i;
LNode *s, *r;
L = (LinkList) malloc(sizeof(LNode)); //创建头节点
r = L; //初始化,设置一个尾指针
printf("请输入单链表的长度:");
scanf("%d", &len);
printf("\n");
for(i = 0; i < len; i++)
{
printf("请输入单链表中节点的值:");
scanf("%d", &x);
s = (LNode *) malloc(sizeof(LNode)); //创建新节点
s->data = x;
r->next = s;
r = s;
printf("\n");
}
r->next = NULL; //尾指针指向空
return L;
}
//单链表的遍历
void TraversalList(LinkList L)
{
LNode *p = L->next;
while(p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
//单链表的插入操作
LNode* ListInsert(LinkList L, int pos, int ele) //按位插入
{
LNode *p; //指针p指向当前扫描到的节点
int j = 0; //当前p指向第几个节点,默认头节点为第0个节点
p = L; //此时p指向单链表的头节点
//寻找第pos-1个节点
while(p!=NULL && j<pos-1)
{
p = p->next; //指向下一个节点
j++;
}
//插入操作
LNode *s = (LNode *) malloc(sizeof(LNode));
s->data = ele;
s->next = p->next;
p->next = s;
return s;
}
void ListInsertNext(LNode *p, int ele) //某个节点后插入
{
if(p == NULL)
{
return;
}
LNode *ss = (LNode *) malloc(sizeof(LNode));
ss->data = ele;
ss->next = p->next;
p->next = ss;
}
void ListInsertPrior(LNode *p, int ele) //某个节点前插入
{
if(p == NULL)
{
return;
}
LNode *sss = (LNode *) malloc(sizeof(LNode));
sss->next = p->next;
p->next = sss; //指针不变,将值交换
sss->data = p->data;
p->data = ele;
}
//单链表的删除操作
void ListDeletePos(LinkList L, int pos, int *ele) //按位删除
{
LNode *p;
int j = 0;
p = L;
while(p!=NULL && j<pos-1)
{
p = p->next; //指向下一个节点
j++;
}
LNode *q = p->next; //指针q指向被删除的节点
*ele = q->data; //返回被删除节点的数据
p->next = q->next; //将p的指针域指向q的指针域
free(q); //释放q指针
}
void ListDeleteVal(LNode *p, int *ele) //按值删除
{
LNode *q = p->next; //指针q指向被删除节点的后一个节点
*ele = p->data;
p->data = p->next->data; //将后一个节点的数据返回到被删除节点的数据
p->next = q->next; //将p的指针域指向q的指针域
free(q); //释放q指针
}
//链表的查找操作
int ListFindPos(LinkList L, int pos) //按位查找
{
int j = 0;
LNode *p;
p = L;
if(pos < 0)
{
return;
}
while(p!=NULL && j<pos)
{
p = p->next;
j++;
}
return p->data;
}
int ListFindVal(LinkList L, int ele) //按值查找
{
LNode *p = L->next;
while(p!=NULL && p->data!=ele)
{
p = p->next;
}
return p->data;
}
int main()
{
LinkList L; //声明一个指向单链表的指针
//L = BuildListHead(L); //使用头插法建立一个单链表
L = BuildListTail(L); //使用尾插法建立一个单链表
TraversalList(L); //遍历整个单链表
printf("-----------------------------\n");
LNode *m;
m = ListInsert(L, 3, 99); //在单链表中按位序插入节点,位置3插入节点,数据为99
TraversalList(L); //遍历整个单链表
ListInsertNext(m, 88); //在单链表中s节点后插入另一个节点ss,数据为88
TraversalList(L); //遍历整个单链表
ListInsertPrior(m, 77); //在单链表中s节点前插入另一个节点sss,数据为77
TraversalList(L); //遍历整个单链表
printf("-----------------------------\n");
int num1, num2;
LNode *n;
ListDeletePos(L, 3, &num1); //删除单链表中的节点,删除位置3上的节点并输出被删除的数据
printf("被删除的元素是:%d\n", num1);
TraversalList(L); //遍历整个单链表
n = ListInsert(L, 3, 100); //在单链表中按位序插入节点,位置3插入节点,数据为100
TraversalList(L); //遍历整个单链表
ListDeleteVal(n, &num2); //删除单链表中的节点,删除指定节点并输出被删除的数据
printf("被删除的元素是:%d\n", num2);
TraversalList(L); //遍历整个单链表
printf("-----------------------------\n");
int data1;
data1 = ListFindPos(L, 3); //按位查找链表的节点,查找位置3上的节点并输出节点数据
printf("查找节点的数据是:%d\n", data1);
printf("-----------------------------\n");
int data2;
data2 = ListFindVal(L, 88); //按值查找顺序表的元素,查找值为88的元素并输出
printf("查找节点的数据是:%d\n", data2);
printf("-----------------------------\n");
return 0;
}
注:单链表的建立有头插法和尾插法,头插法可以实现单链表的逆序。
单链表基本操作的分析
注:按顺序表基本操作分析的方法进行分析,步骤相同,下面只给出平均情况下的时间复杂度。
插入操作的时间复杂度
- 平均情况:时间复杂度O(n)
删除操作的时间复杂度
- 平均情况:时间复杂度O(n)
查找操作的时间复杂度
按值查找:
- 平均情况:时间复杂度O(n)
按位查找:
- 平均情况:时间复杂度O(n)
单链表的特点
- 顺序存取,不可随机存取
- 需要一定空间存放指针
- 不要求连续的存储空间
- 扩展容量方便