带头结点单链表
最后一个节点的next值为NULL(结尾标识)
头结点:
一个标识,不存放数据,不参与具体运算
数据节点:
存放数据,第一个数据节点的下标为0优点:
在已知位置插入数据时间复杂度O(1)
在已知位置删除数据时间复杂度O(1)
缺点:
查找数据O(n),不支持随机访问
该顺序表由list.h + list.cpp + main.cpp 组成
list.h文件
#pragma once
typedef int ElemType;
typedef struct Node
{
ElemType data;//数据
struct Node* next;//下一个节点的地址
}Node, * List;//List == Node *
//初始化
void InitList(List plist);
//销毁
void Destroy(List plist);
//清空数据
void Clear(List plist);
//查找val,找到返回节点地址,失败返回NULL
Node* Search(const List plist, ElemType val);
//判断是否为空
bool IsEmpty(const List plist);
//获取长度,有效数据个数
int GetLength(const List plist);
//获取i下标的值 ,不重要
bool GetElem(const List plist, int i, ElemType* rtval);
//获取i下标的前驱
bool GetPrio(const List plist, int i, ElemType* rtval);
//获取i下标的后继
bool GetNext(const List plist, int i, ElemType* rtval);
//在i下标插入数据val
bool InsertPos(List plist, int i, ElemType val);
//头插,将新数据插入在第一个位置
bool Insert_head(List plist, ElemType val);
//尾插
bool Insert_tail(List plist, ElemType val);
//删除i下标的值
bool DelPos(List plist, int i, ElemType* rtval);
//删除val
bool DelVal(List plist, ElemType val);
//输出
void Show(const List plist);
//逆置
void Reverse(List plist);
list.cpp文件
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "list.h"
//链表中最常用的两种循环
//for(p=plist;p->next!=NULL;p=p->next);//需要前驱信息,例如插入,删除等
//for(p=plist->next;p!=NULL;p=p->next);//遍历数据节点,例如,长度,Show,查找等
//初始化
void InitList(List plist)//Node *
{
assert(plist != NULL);
if (plist == NULL)
return;
//头结点的数据不使用
plist->next = NULL;
}
//头插,将新数据插入在第一个位置
bool Insert_head(List plist, ElemType val)
{
//创建新节点
//Node x;//错误的,必须使用动态内存
Node* p = (Node*)malloc(sizeof(Node)); //不使用List p
assert(p != NULL);
//将val存到新节点中
p->data = val;
//将新节点插入到第一个位置
p->next = plist->next;//1
plist->next = p;//2
return true;
}
//尾插,新数据插在最后
bool Insert_tail(List plist, ElemType val)
{
//利用p找尾部
Node* p;
for (p = plist; p->next != NULL; p = p->next)
;
//创建新节点
Node* q = (Node*)malloc(sizeof(Node));
assert(q != NULL);
//val赋值给新节点
q->data = val;
//将新节点q插入在p的后面
q->next = p->next;//q->next = NULL;
p->next = q;
return true;
}
//查找val,找到返回节点地址,失败返回NULL
Node* Search(const List plist, ElemType val)
{
//遍历所有的数据节点
for (Node* p = plist->next; p != NULL; p = p->next)
{
if (p->data == val)
return p;
}
return NULL;
}
//判断是否为空
bool IsEmpty(const List plist)
{
return plist->next == NULL;
}
//获取长度,有效数据个数
int GetLength(const List plist)
{
int count = 0;
for (Node* p = plist->next; p != NULL; p = p->next)
count++;
return count;
}
//获取i下标的值
bool GetElem(const List plist, int i, ElemType* rtval)
{
if (i < 0 || i >= GetLength(plist))
return false;
Node* p = plist->next;
for (int j = 0; j < i; j++)
p = p->next;
if (rtval != NULL)
*rtval = p->data;
return true;
}
//获取i下标的前驱
bool GetPrio(const List plist, int i, ElemType* rtval)
{
return GetElem(plist, i - 1, rtval);
}
//获取i下标的后继
bool GetNext(const List plist, int i, ElemType* rtval)//const Node *plist;
{
return GetElem(plist, i + 1, rtval);
}
//在i下标插入数据val
bool InsertPos(List plist, int i, ElemType val)
{
if (i<0 || i>GetLength(plist))
{
return false;
}
Node* p = plist;
for (int j = 0; j < i; j++)
p = p->next;
Node* q = (Node*)malloc(sizeof(Node));
assert(q != NULL);
q->data = val;
//将q插入在p的后面
q->next = p->next;
p->next = q;
return true;
}
//删除i下标的值
bool DelPos(List plist, int i, ElemType* rtval)
{
if (i < 0 || i >= GetLength(plist))
return false;
Node* p = plist;
for (int j = 0; j < i - 1; j++)
p = p->next;
//删除p后面的节点q p->next=p->next->next; free(p->next);错误
Node* q = p->next;
//将q剔除
p->next = q->next;
//free(q);
return true;
}
//查找val的前驱
static Node* SearchPri(List plist, ElemType val)
{
for (Node* p = plist; p->next != NULL; p = p->next)
{
if (p->next->data == val)
return p;
}
return NULL;
}
//删除val
bool DelVal(List plist, ElemType val)
{
Node* p = SearchPri(plist, val);
if (p == NULL)
return false;
Node* q = p->next;
p->next = q->next;//p->next = p->next->next;
free(q);
return true;
}
//输出
void Show(const List plist)
{
//遍历数据节点
for (Node* p = plist->next; p != NULL; p = p->next)
{
printf("%d ", p->data);
}
printf("\n");
}
//销毁:删除所有的数据节点
//算法2总是删除第一个数据节点
void Destroy(List plist)
{
Node* p;
while (plist->next != NULL)
{
p = plist->next;
plist->next = p->next;
free(p);
}
}
//算法1
void Destroy1(List plist)
{
//释放所有的数据节点(头结点不是动态创建)
Node* p = plist->next;
Node* q;
while (p != NULL)
{
q = p->next;
free(p);
p = q;
}
plist->next = NULL;
}
//清空数据
void Clear(List plist)
{
Destroy(plist);
}
//逆置 .利用头插算法,将所有节点重新插入一遍 (最好)
void Reverse(List plist)//O(n),O(1)
{
if (plist == NULL || plist->next == NULL || plist->next->next == NULL)
return;
Node* p = plist->next;
Node* q;
plist->next = NULL;
while (p != NULL)
{
q = p->next;
//p头插到plist
p->next = plist->next;
plist->next = p;
p = q;
}
}
main.cpp文件///测试
#include <stdio.h>
#include "list.h"
//#include<vld.h>
int main()
{
Node head;//头结点
InitList(&head);
for (int i = 0; i < 10; i++)
{
//Insert_head(&head, i);
Insert_tail(&head, i);
}
Show(&head);
printf("\n");
for (int i = -1; i < 20; i++)
{
Node* p = Search(&head, i);
if (p != NULL)
printf("%d ", p->data);
}
printf("\n");
printf("%d\n ", GetLength(&head));
printf("删除5这个值\n");
DelVal(&head, 5);
Show(&head);
printf("删除下标为5的值\n");
ElemType* rtval = {};
DelPos(&head, 5, rtval);
Show(&head);
printf("销毁数据\n");
Destroy(&head);
Show(&head);
/*Destroy(&head);*/
return 0;
}