1.线性表是最常用且最简单的一种数据结构。
1>存在唯一的一个北城做“第一个”的数据元素;
2>存在唯一的一个被称做“最后一个”的数据元素;
3>除第一个之外,集合中的每个数据元素均只有一个前驱;
4>除最后一个之外,集合中每个数据元素均只有一个后继。
2.定长顺序表
sqlist.h
#pragma once
//定长的顺序表结构体定义
typedef struct Sqlist
{
int arr[10];//数据域
int length;//有效数据节点的个数
}Sqlist, *PSqlist;
//typedef struct Sqlist Sqlist;
//typedef struct Sqlist* PSqlist;
//初始化
void Init_sqlist(PSqlist plist);//struct Sqlist* plist
//按位置插入
bool Insert_Pos(PSqlist plist, int pos, int val);
//按值删 删除这个值出现的第一次的位置
bool Del_val(PSqlist plist, int val);
//按位置删除
bool Del_pos(PSqlist plist, int pos);
//查找值为val的节点
int Search(PSqlist plist, int val);
//判空
bool IsEmpty(PSqlist plist);
//判满
bool IsFull(PSqlist plist);
//清空
void Clear(PSqlist plist);
//销毁
void Destroy(PSqlist plist);
sqlist.cpp
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "sqlist.h"
//初始化
void Init_sqlist(PSqlist plist)//struct Sqlist* plist
{
assert(plist != NULL);
if(plist == NULL)
return;
//plist->arr;//不需要初始化
plist->length = 0;
}
//按位置插入
bool Insert_Pos(PSqlist plist, int pos, int val)
{
//assert
if(pos<0 || pos>plist->length)
return false;
if(IsFull(plist))
{
return false;
}
for(int i=plist->length-1; i>=pos; i--)
{
plist->arr[i+1] = plist->arr[i];
}
//此时 for循环结束 标志着插入位置已经清空 将位置留出来了
plist->arr[pos] = val;
plist->length++;
return true;
}
//按值删 删除这个值出现的第一次的位置
bool Del_val(PSqlist plist, int val)
{
assert(plist != NULL);
if(plist == NULL)
return false;
//如果为NULL 则不许删除
if(IsEmpty(plist))
{
return false;
}
int pos = Search(plist, val);//i是Search的返回值
if(pos == -1)//pos==-1 代表没找到 则返回假
{
return false;
}
//for(int i=pos; i+1<plist->length; i++)
//{
// plist->arr[i] = plist->arr[i+1];
//}
此时for循环执行完毕 代表待删除节点后的值统一向前覆盖了
//plist->length--;//删除了一个节点 则有效长度length-- 一下
Del_pos(plist, pos);
return true;
}
//按位置删除
bool Del_pos(PSqlist plist, int pos)
{
assert(plist!=NULL && pos>=0 && pos<plist->length);
if(plist==NULL || pos<0 || pos>=plist->length)
{
return false;
}
//if(IsEmpty(plist)); 这一行代码 执行不到 无意义
for(int i=pos; i+1<plist->length; i++)
{
plist->arr[i] = plist->arr[i+1];
}
//此时for循环执行完毕 代表待删除节点后的值统一向前覆盖了
plist->length--;//删除了一个节点 则有效长度length-- 一下
return true;
}
//查找值为val的节点
int Search(PSqlist plist, int val)
{
assert(plist!=nullptr);
for(int i=0; i<plist->length; i++)
{
if(plist->arr[i] == val)
{
return i;
}
}
return -1;
}
//判空
bool IsEmpty(PSqlist plist)
{
return plist->length==0;
}
//判满
bool IsFull(PSqlist plist)
{
return plist->length==10;
}
//清空
void Clear(PSqlist plist)
{
plist->length = 0;
}
//销毁
void Destroy(PSqlist plist);
3.不定长的顺序表
Dsqlist.h
#pragma once
typedef int ELEM_TYPE;
#define INIT_SIZE 10//默认开始的时候的空间个数
typedef struct DSQlist
{
ELEM_TYPE *data;//用来承接我malloc申请的动态内存
int length;//有效数据长度
int listsize;//保存当前最大容量个数(以sizeof(ELEM_TYPE)为单位)
}DSQlist, *PDSQlist;
//下面写可执行函数声明:
//不外乎增删改查
//初始化
void Init_sqlist(PDSQlist plist);//struct Sqlist* plist
//按位置插入
bool Insert_Pos(PDSQlist plist, int pos, int val);
//按值删 删除这个值出现的第一次的位置
bool Del_val(PDSQlist plist, int val);
//按位置删除
bool Del_pos(PDSQlist plist, int pos);
//查找值为val的节点
int Search(PDSQlist plist, int val);
//判空
bool IsEmpty(PDSQlist plist);
//判满
bool IsFull(PDSQlist plist);
//清空
void Clear(PDSQlist plist);
//销毁
void Destroy(PDSQlist plist);
//扩容
void Inc(PDSQlist plist);//data
void Show(PDSQlist plist);
Dsqlist.cpp
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "Dsqlist.h"
//初始化
void Init_sqlist(PDSQlist plist)//struct Sqlist* plist
{
//assert
plist->data = (ELEM_TYPE*)malloc(sizeof(ELEM_TYPE) * INIT_SIZE);
assert(plist->data != NULL);
plist->length = 0;
plist->listsize = INIT_SIZE;
}
//按位置插入
bool Insert_Pos(PDSQlist plist, int pos, int val)
{
//assert plist pos
//判满 扩容
if(IsFull(plist))
{
Inc(plist);
}
//将pos以及其之后的值统一向后挪动一位 将位置空出来
for(int i=plist->length-1; i>=pos; i--)
{
plist->data[i+1] = plist->data[i];
}
//插入
plist->data[pos] = val;
plist->length++;
return true;
}
//按值删 删除这个值出现的第一次的位置
bool Del_val(PDSQlist plist, int val)
{
//assert
if(IsEmpty(plist))
return false;
int pos = Search(plist, val);
if(pos == -1)
return false;
return Del_pos(plist, pos);
}
//按位置删除
bool Del_pos(PDSQlist plist, int pos)
{
//assert plist pos
//判满操作没有必要 因为对pos的合法性已经做了判断
for(int i=pos; i+1<plist->length; i++)
{
plist->data[i] = plist->data[i+1];
}
plist->length--;
return true;
}
//查找值为val的节点
int Search(PDSQlist plist, int val)
{
//assert
for(int i=0; i<plist->length; i++)
{
if(plist->data[i] == val)
{
return i;
}
}
return -1;
}
//判空
bool IsEmpty(PDSQlist plist)
{
return plist->length == 0;
}
//判满
bool IsFull(PDSQlist plist)
{
return plist->length == plist->listsize;
}
//清空
void Clear(PDSQlist plist)
{
plist->length = 0;
}
//销毁
void Destroy(PDSQlist plist)
{
free(plist->data);
plist->data = NULL;
plist->length = plist->listsize = 0;
}
//扩容
void Inc(PDSQlist plist)//data
{
//malloc realloc calloc
//库容 realloc
plist->data = (ELEM_TYPE*)realloc(plist->data, sizeof(ELEM_TYPE) * plist->listsize * 2);
if(plist->data == NULL)
{
return;
}
//plist->length; 有效值不需要改变
plist->listsize *= 2;
}
void Show(PDSQlist plist)
{
//assert
for(int i=0; i<plist->length; i++)
{
printf("%d ", plist->data[i]);
}
printf("\n");
}
顺序表的特点:
1.简单 好实现
2.逻辑上相邻,物理上也相邻
3.插入时间复杂度O(n)(挪动数据) 尾插的时间复杂度为O(1)
4.删除时间复杂度O(n)(挪动数据) 尾删的时间复杂度为O(1)
5.随机访问时间复杂度O(1) 因为有数组下标帮忙
4.单链表(带头结点)
特点:逻辑上相邻,但物理上不一定相邻
两种for循环
1>for(Node* p=plist;p!=NULL;p=p->next)
2>for(Node* p=plist->next;p!=NULL;p=p->next)
list.h
#pragma once
//带头结点的单链表的结构体设计:
typedef struct Node
{
int data;//数据域 保存节点有效值
struct Node *next;//指针域 指向下一个节点的地址
}Node, *PNode;
/* 如果头结点想单独设计 则为下面代码
struct Head
{
struct Node* next;
};
*/
//所以头结点有两种处理方案:
//1.给头结点单独设计一套结构体
//2.使用普通数据节点的结构体设计 其中的数据域不使用 只使用其指针域
//可以实现的操作 声明
//增删改查
//初始化函数(给头结点赋值)
void Init_list(PNode plist);
//头插
bool Insert_Head(PNode plist, int val);//O(1)
//尾插
bool Insert_Tail(PNode plist, int val);//O(n)
//按位置插 pos
bool Insert_pos(PNode plist, int pos, int val);//O(n)
//头删
bool Del_Head(PNode plist);//O(1)
//尾删
bool Del_Tail(PNode plist);//O(n)
//按位置删 pos
bool Del_Pos(PNode plist, int pos);//O(n)
//按值删(将值为val的第一个节点删除掉)
bool Del_val(PNode plist, int val);//O(n)
//查找 val 在plist这个单链表中找值为val的第一个节点 找到后返回其地址 不然返回NULL
struct Node * Search(PNode plist, int val);//O(n)
//判空
bool IsEmpty(PNode plist);
//判满 单链表不需要判满
//获取前驱 (将值为val的节点去前一个节点返回)
struct Node* Get_Prior(PNode plist, int val);
//获取后继 (将值为val的节点去后一个节点返回)
struct Node* Get_Next(PNode plist, int val);
//获取其有效节点个数
int Get_Length(PNode plist);
//清空
void Clear(PNode plist);
//销毁1
void Destroy1(PNode plist);
//销毁2
void Destroy2(PNode plist);
void Show(PNode plist);
list.cpp
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "list.h"
//初始化函数(给头结点赋值)
void Init_list(PNode plist)
{
assert(plist != NULL);
if(plist == NULL)
{
return;
}
//plist->data; 头结点的数据域不使用
plist->next = NULL;
}
//头插
bool Insert_Head(PNode plist, int val)
{
assert(plist!=nullptr);
//1.创建一个新节点
struct Node * pnewnode = (Node*)malloc(sizeof(Node) * 1);
assert(pnewnode != NULL);
pnewnode->data = val;
pnewnode->next = NULL;//此行代码 可加可不加 因为紧接着就会对pnewnode->next 重新赋值
//2.找到合适的插入位置(头插函数 这一步不需要处理)
//3.插入新节点 1和2 顺序不能颠倒
pnewnode->next = plist->next;//1
plist->next = pnewnode;//2
return true;
}
//尾插
bool Insert_Tail(PNode plist, int val)
{
assert(plist!=nullptr);
//1.创建新节点
PNode pnewnode = (PNode)malloc(sizeof(Node) * 1);
assert(pnewnode != NULL);
pnewnode->data = val;
pnewnode->next = NULL; //这一步需不需要? 如果56行代码有的话 则不需要 没有的话这一行代码就必须写
//2.先找到最后一个节点
Node *p=plist;
for(p; p->next!=NULL; p=p->next); //此时for循环结束 p指向的是尾结点
//3.给它后面插入
pnewnode->next = p->next;
p->next = pnewnode;
return true;
}
//按位置插 pos
bool Insert_pos(PNode plist, int pos, int val)
{
assert(plist!=NULL && pos>=0 && pos<=Get_Length(plist));
if(plist == NULL || pos<0 || pos>Get_Length(plist))
return false;
//1.创建新节点
PNode pnewnode = (PNode)malloc(sizeof(Node) * 1);
assert(pnewnode != NULL);
pnewnode->data = val;
//2.找到合适的插入位置
Node*p = plist;
for(int i=0; i<pos; i++)
{
p=p->next;
}
//此时 p就指向待插入位置的前驱
//3.给它后面插入
pnewnode->next = p->next;
p->next = pnewnode;
return true;
}
//头删
bool Del_Head(PNode plist)
{
assert(plist!=nullptr);
if(IsEmpty(plist))
return false;
//1.申请一个临时变量指向要删除的第一个有效节点
Node *p = plist->next;
//2.将要删除的节点左右连接起来
plist->next = p->next;//plist->next = plist->next->next;
//3.将要删除的节点内存释放,防止内存泄露
free(p);
p = NULL;
return true;
}
//尾删
bool Del_Tail(PNode plist)
{
assert(plist!=nullptr);
//1.先找到要删除节点的上一个节点(前驱)
Node *p =plist;
for(p; p->next!=NULL; p=p->next)
{
if(p->next->next == NULL)
{
break;
}
}
//此时p就指向倒数第二个节点
//2.申请一个临时变量指向要删除的有效节点
Node *q = p->next;
//3.跨越指向(将要删除的节点左右连接起来)
p->next = q->next;//p->next = p->next->next;
//4.释放内存
free(q);
return true;
}
//按位置删 pos
bool Del_Pos(PNode plist, int pos)
{
assert(plist!=NULL && pos>=0 && pos<Get_Length(plist));
if(plist==NULL || pos<0 || pos>=Get_Length(plist))
return false;
//1.找到删除节点的上一个节点
Node *p = plist;
for(int i=0; i<pos; i++)
{
p = p->next;
}
//此时p就指向pos指向节点的前一个节点
//2.申请一个临时变量指向要删除的有效节点
Node *q = p->next;
//3.跨越指向(将要删除的节点左右连接起来)
p->next = q->next;
//4.释放内存
free(q);
return true;
}
//按值删(将值为val的第一个节点删除掉)
bool Del_val(PNode plist, int val)
{
assert(plist!=nullptr);
Node *p = Get_Prior(plist, val);
if(p==NULL)
{
return false;
}
Node *q = p->next;
p->next = q->next;
free(q);
q = NULL;
return true;
}
//查找 val 在plist这个单链表中找值为val的第一个节点 找到后返回其地址 不然返回NULL
struct Node * Search(PNode plist, int val)
{
//不需要前驱的操作 所以 用第二种for循环
assert(plist!=nullptr);
Node *p = plist->next;
for(p; p!=NULL; p=p->next)
{
if(p->data == val)
{
return p;
}
}
return NULL;
}
//判空
bool IsEmpty(PNode plist)
{
return plist->next == NULL;
}
//判满 单链表不需要判满
//获取前驱 (将值为val的节点去前一个节点返回)
struct Node* Get_Prior(PNode plist, int val)
{
assert(plist!=nullptr);
Node *p=plist;
for(p; p->next!=NULL; p=p->next)
{
if(p->next->data == val)
{
return p;
}
}
return NULL;
}
//获取后继 (将值为val的节点去后一个节点返回)
struct Node* Get_Next(PNode plist, int val)
{
Node *p = Search(plist, val);
if(p==NULL)
{
return NULL;
}
return p->next;
}
//获取其有效节点个数
int Get_Length(PNode plist)
{
assert(plist!=nullptr);
int count = 0;
Node *p = plist->next;
for(p; p!=NULL; p=p->next)
{
count++;
}
return count;
}
//清空
void Clear(PNode plist)
{
Destroy1(plist);
}
//销毁1 一致头删 这个一定掌握(只用了一个指针)
void Destroy1(PNode plist)
{
assert(plist!=nullptr);
/*while(!IsEmpty(plist))
{
Del_Head(plist);
}*/
while(plist->next != NULL)
{
Node *p = plist->next;
plist->next = p->next;
free(p);
p = NULL;
}
}
//销毁2 不借助头节点(两个指针) 能理解就理解 理解不了记住第一种头删的方式即可
void Destroy2(PNode plist)
{
assert(plist!=nullptr);
Node *p = plist->next;
Node *q;
while(p != NULL)
{
q = p->next;
free(p);
p = q;
}
plist->next = NULL;
}
void Show(PNode plist)
{
assert(plist!=nullptr);
Node *p = plist->next;
for(p; p!=NULL; p=p->next)
{
printf("%d ", p->data);
}
printf("\n");
}
5.单链表(不带头结点)
两种for循环
1>for(Node* p=phead;p!=NULL;p=p->next)
2>for(Node* p=phead->next;p!=NULL;p=p->next)
NoHeadList.h
#pragma once
//不带头结点的单链表的结构体设计:
typedef struct Node
{
int data;//数据域 保存节点有效值
struct Node *next;//指针域 指向下一个节点的地址
}Node, *PNode;
//增删改查
//初始化函数(给头结点赋值)
void No_Head_Init_list(PNode *phead);
//头插
bool No_Head_Insert_Head(PNode *phead, int val);
//尾插
bool No_Head_Insert_Tail(PNode *phead, int val);
//按位置插 pos
bool No_Head_Insert_pos(PNode *phead, int pos, int val);
//头删
bool No_Head_Del_Head(PNode *phead);
//尾删
bool No_Head_Del_Tail(PNode *phead);
//按位置删 pos
bool No_Head_Del_Pos(PNode *phead, int pos);
//按值删(将值为val的第一个节点删除掉)
bool No_Head_Del_val(PNode *phead, int val);
//查找 val 在phead这个单链表中找值为val的第一个节点 找到后返回其地址 不然返回NULL
struct Node * No_Head_Search(PNode *phead, int val);
//判空
bool No_Head_IsEmpty(PNode *phead);
//判满 单链表不需要判满
获取前驱 (将值为val的节点去前一个节点返回)
//struct No_Head_Node* Get_Prior(PNode *phead, int val);
//
获取后继 (将值为val的节点去后一个节点返回)
//struct Node* No_Head_Get_Next(PNode *phead, int val);
//获取其有效节点个数
int No_Head_Get_Length(PNode *phead);
//清空
void No_Head_Clear(PNode *phead);
//销毁1
void No_Head_Destroy1(PNode *phead);
//销毁2
void No_Head_Destroy2(PNode *phead);
void No_Head_Show(PNode *phead);
NoHeadList.cpp
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "NoHeadList.h"
//增删改查
//初始化函数(给头指针赋初始值)
void No_Head_Init_list(PNode *phead)//Pnode * == Node **
{
*phead = NULL;//*phead 保存的是第一个有效数据的地址 不确定的情况下NULL
}
//头插
bool No_Head_Insert_Head(PNode *phead, int val)
{
assert(phead!=nullptr);
//1.创建新节点
Node *pnewnode = (Node*)malloc(sizeof(Node) * 1);
assert(pnewnode != NULL);
pnewnode->data = val;
//2.找到合适的插入位置 头插
//3.插入
//带头结点的代码
//pnewnode->next = plist->next;
//plist->next = pnenwode;
pnewnode->next = *phead;
*phead = pnewnode;
return true;
}
//尾插
bool No_Head_Insert_Tail(PNode *phead, int val)
{
assert(phead!=nullptr);
/*Node *p = plist;
for(p; p->next!=NULL; p=p->next);*/
if(*phead == NULL)
{
return No_Head_Insert_Head(phead, val);
}
//此时保证至少有一个数据节点
Node *p = *phead; //NULL
for(p; p->next!=NULL; p=p->next);
Node *pnewnode = (Node*)malloc(sizeof(Node) * 1);
assert(pnewnode != NULL);
pnewnode->data = val;
pnewnode->next = p->next;
p->next = pnewnode;
return true;
}
//按位置插 pos
bool No_Head_Insert_pos(PNode *phead, int pos, int val)
{
assert(phead!=NULL && pos>=0 && pos<=No_Head_Get_Length(phead));
if(pos == 0)//pos == 0 调用头插
{
return No_Head_Insert_Head(phead, val);
}
//下面的这个if 不会影响最终结果 只是为了和上面保持一致
if(pos == No_Head_Get_Length(phead))//pos == length 调用尾插
{
return No_Head_Insert_Tail(phead, val);
}
Node *pnewnode = (Node*)malloc(sizeof(Node) * 1);
assert(pnewnode != NULL);
pnewnode->data = val;
Node *p = *phead;
for(int i=1; i<pos; i++)
{
p=p->next;
}
pnewnode->next = p->next;
p->next = pnewnode;
return true;
}
//头删
bool No_Head_Del_Head(PNode *phead)
{
assert(phead!=nullptr);
if(No_Head_IsEmpty(phead))
{
return false;
}
Node *q = *phead;
*phead = q->next;
free(q);
return true;
}
//尾删
bool No_Head_Del_Tail(PNode *phead)
{
assert(phead!=nullptr);
if(No_Head_IsEmpty(phead))
{
return false;
}
if((*phead)->next == NULL)//这个if如果为真 代表只有一个有效数据节点
{
return No_Head_Del_Head(phead);
}
Node *p = *phead;
for(p; p->next->next!=NULL; p=p->next);
Node *q = p->next;
p->next = q->next;
free(q);
return true;
}
//按位置删 pos
bool No_Head_Del_Pos(PNode *phead, int pos)
{
assert(phead!=NULL && pos>=0 && pos<No_Head_Get_Length(phead));
if(pos == 0)
{
return No_Head_Del_Head(phead);
}
Node *p = *phead;
for(int i=1; i<pos; i++)
{
p=p->next;
}
Node *q = p->next;
p->next = q->next;//p->next = p->next->next;
free(q);
return true;
}
//按值删(将值为val的第一个节点删除掉)
bool No_Head_Del_val(PNode *phead, int val)
{
assert(phead!=nullptr);
if(No_Head_IsEmpty(phead))
{
return false;
}
if((*phead)->data == val)
{
return No_Head_Del_Head(phead);
}
Node *p = *phead;
for(p; p->next!=NULL; p=p->next)
{
if(p->next->data == val)
{
Node *q = p->next;
p->next = q->next;
free(q);
return true;
}
}
return false;
}
//查找 val 在phead这个单链表中找值为val的第一个节点 找到后返回其地址 不然返回NULL
struct Node * No_Head_Search(PNode *phead, int val)
{
Node *p = *phead;
for(p; p!=NULL; p=p->next)
{
if(p->data == val)
{
return p;
}
}
return NULL;
}
//判空
bool No_Head_IsEmpty(PNode *phead)
{
return *phead == NULL;
}
//判满 单链表不需要判满
获取前驱 (将值为val的节点去前一个节点返回)
//struct No_Head_Node* Get_Prior(PNode *phead, int val);
//
获取后继 (将值为val的节点去后一个节点返回)
//struct Node* No_Head_Get_Next(PNode *phead, int val);
//获取其有效节点个数
int No_Head_Get_Length(PNode *phead)
{
int count = 0;
Node *p = *phead;
for(p; p!=NULL; p=p->next)
{
count++;
}
return count;
}
//清空
void No_Head_Clear(PNode *phead)
{
No_Head_Destroy1(phead);
}
//销毁1
void No_Head_Destroy1(PNode *phead)
{
/*while(!No_Head_IsEmpty(phead))
{
No_Head_Del_Head(phead);
}*/
while(*phead != NULL)
{
Node *q = *phead;
*phead = q->next;
free(q);
}
}
//销毁2
void No_Head_Destroy2(PNode *phead)
{
Node *p = *phead;
Node *q;
while(p!=NULL)
{
q = p->next;
free(p);
p = q;
}
*phead = NULL;
}
void No_Head_Show(PNode *phead)
{
Node *p = *phead;
for(p; p!=NULL; p=p->next)
{
printf("%d ", p->data);
}
printf("\n");
}
6.双向链表
两种for循环
1>for(DNode* p=plist;p!=NULL;p=p->next)
2>for(DNode* p=plist->next;p!=NULL;p=p->next)
Dlist.h
#pragma once
//双向链表结构体设计
typedef struct DNode
{
int data;//数值域
struct DNode* next;//保存下一个节点的地址
struct DNode* prior;//保存上一个节点的地址
}DNode, *PDNode;
//初始化
void Init_Dlist(struct DNode *plist);
//头插
bool InsertHead(PDNode plist, int val);
//尾插
bool InsertTail(PDNode plist, int val);
//按位置插
bool InsertPos(PDNode plist, int pos, int val);
//头删
bool DelHead(PDNode plist);
//尾删
bool DelTail(PDNode plist);
//按位置删
bool DelPos(PDNode plist, int pos);
//按值删
bool DelVal(PDNode plist, int val);
//查找
struct DNode *Search(PDNode plist, int val);
//判空
bool IsEmpty(PDNode plist);
//获取有效元素个数
int Get_length(PDNode plist);
//打印
void Show(PDNode plist);
//清空
void Clear(PDNode plist);
//销毁
void Destroy1(PDNode plist);
void Destroy2(PDNode plist);
Dlist.cpp
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "Dlist.h"
//初始化
void Init_Dlist(struct DNode *plist)
{
assert(plist != NULL);
if(NULL == plist)
return;
//plist->data; 头结点的数据域不需要赋值
plist->next = NULL;
plist->prior = NULL;
}
//头插
bool InsertHead(PDNode plist, int val)
{
assert(plist != NULL);
//1.创建新节点
DNode *pnewnode = (DNode*)malloc(sizeof(DNode) * 1);
assert(pnewnode != NULL);
pnewnode->data = val;
//2.找到合适的插入位置 头插
//3.插入 3421
pnewnode->next = plist->next;//自身的next
pnewnode->prior = plist;//自身的priro
if(pnewnode->next != NULL)//保证下一个节点存在
{//下一个节点的前驱指针
pnewnode->next->prior = pnewnode;//plist->next->prior = pnewnode;
}
plist->next = pnewnode;//上一个节点的后继指针
return true;
/*
node->next = plist->next;
pnewnode->prior = plist;
plist->next = pnewnode;
if(pnewnode->next!=NULL)
{
pnewnode->next->prior = pnewnode;// 区别:和上面比,(这快这行代码失效:plist->next->prior = pnewnode;) 因为plist->next 指向已经修改了
}
*/
}
//尾插
bool InsertTail(PDNode plist, int val)
{
assert(plist != NULL);
DNode *pnewnode = (DNode*)malloc(sizeof(DNode) * 1);
assert(pnewnode != NULL);
pnewnode->data = val;
DNode *p = plist;
for(p; p->next!=NULL; p=p->next);
pnewnode->next = p->next;
pnewnode->prior = p;
//因为是尾插操作 所以下一个节点的前驱不需要修改
p->next = pnewnode;
return true;
}
//按位置插
bool InsertPos(PDNode plist, int pos, int val)
{
assert(plist != NULL&&pos>=0&&pos<=Get_length(plist));
//将只需要处理三条线的特殊情况 提前处理掉
if(pos == 0)//如果pos为0 并且plist是个空链 则只需要改变三条线
{
return InsertHead(plist, val);
}
if(pos == Get_length(plist))//如果pos ==length 则一定第三条线不存在
{
return InsertTail(plist, val);
}
DNode *pnewnode = (DNode*)malloc(sizeof(DNode) * 1);
assert(pnewnode != NULL);
pnewnode->data = val;
DNode *p = plist;
for(int i=0; i<pos; i++)
{
p=p->next;
}
pnewnode->next = p->next;
pnewnode->prior = p;
pnewnode->next->prior = pnewnode;
p->next = pnewnode;
return true;
}
//头删
bool DelHead(PDNode plist)
{
assert(plist != NULL);
if(IsEmpty(plist))
return false;
DNode *q = plist->next;
plist->next = q->next;//修改上一个节点的后继
if(q->next != NULL)//保证待删除节点后面的节点存在
{
q->next->prior = plist;//修改下一个节点的前驱
}
free(q);
return true;
}
//尾删
bool DelTail(PDNode plist)
{
assert(plist != NULL);
if(IsEmpty(plist))
return false;
DNode *q = plist;
for(q; q->next!=NULL; q=q->next);
DNode *p = q->prior;
p->next = q->next;// p->next = NULL;
//下一个节点的前驱 不需要修改 因为是尾删 q已经指向的是最后一个节点了
return true;
}
//按位置删
bool DelPos(PDNode plist, int pos)
{
assert(plist!=NULL && pos>=0 && pos<Get_length(plist));
if(pos == 0)
{
return DelHead(plist);
}
if(pos == Get_length(plist)-1)
{
return DelTail(plist);
}
DNode *p = plist;
for(int i=0; i<pos; i++)
{
p=p->next;
}
DNode *q = p->next;
p->next = q->next;//上一个节点的next
q->next->prior = p;//下一个节点的prior
free(q);
return true;
}
//按值删
bool DelVal(PDNode plist, int val)
{
DNode *q = plist->next;
for(q; q!=NULL; q=q->next)
{
if(q->data == val)
{
DNode *p = q->prior;
p->next = q->next;
if(q->next != NULL)
{
q->next->prior = p;
}
free(q);
return true;
}
}
return false;
}
//查找
struct DNode *Search(PDNode plist, int val)
{
DNode *p = plist->next;
for(p; p!=NULL; p=p->next)
{
if(p->data == val)
{
return p;
}
}
return NULL;
}
//判空
bool IsEmpty(PDNode plist)
{
return plist->next == NULL;
}
//获取有效元素个数
int Get_length(PDNode plist)
{
int count = 0;
DNode *p = plist->next;
for(p; p!=NULL; p=p->next)
{
count++;
}
return count;
}
//打印
void Show(PDNode plist)
{
DNode *p = plist->next;
for(p; p!=NULL; p=p->next)
{
printf("%d ", p->data);
}
printf("\n");
}
//清空
void Clear(PDNode plist)
{
Destroy1(plist);
}
//销毁
void Destroy1(PDNode plist)
{
while(!IsEmpty(plist))
{
DelHead(plist);
}
plist->next = NULL;
}
void Destroy2(PDNode plist)
{
DNode *p = plist->next;
DNode *q;
while(p!=NULL)
{
q = p->next;
free(p);
p = q;
}
plist->next = NULL;
}
7.(单)循环链表
易错点:
1)尾节点的next指向的是头结点,而不是第一个有效节点
2)单循环链表只能从尾巴找到头,而不能直接从头找到尾巴
3)for循环向后跑的时候,循环结束条件不再是和NULL比较,而是和头结点的地址比较
两种for循环
1>for(CNode* p=plist;p!=plist;p=p->next)
2>for(CNode* p=plist->next;p!=plist;p=p->next)
clist.h
#pragma once
//带头结点的单链表的结构体设计:
typedef int ELEMTYPE;
typedef struct CNode
{
ELEMTYPE data;//数据域 保存节点有效值
struct Node *next;//指针域 指向下一个节点的地址
}Node, *PCNode;
//可以实现的操作 声明
//增删改查
//初始化函数(给头结点赋值)
void Init_clist(struct CNode* plist);
//头插
bool Insert_Head(PCNode plist, int val);
//尾插
bool Insert_Tail(PCNode plist, int val);
//按位置插 pos
bool Insert_pos(PCNode plist, int pos, int val);
//头删
bool Del_Head(PCNode plist);
//尾删
bool Del_Tail(PCNode plist);
//按位置删 pos
bool Del_Pos(PCNode plist, int pos);
//按值删(将值为val的第一个节点删除掉)
bool Del_val(PCNode plist, int val);
//查找 val 在plist这个单链表中找值为val的第一个节点 找到后返回其地址 不然返回NULL
struct CNode * Search(PCNode plist, int val);
//判空
bool IsEmpty(PCNode plist);
//判满 单链表不需要判满
//获取前驱 (将值为val的节点去前一个节点返回)
struct CNode* Get_Prior(PCNode plist, int val);
//获取后继 (将值为val的节点去后一个节点返回)
struct CNode* Get_Next(PCNode plist, int val);
//获取其有效节点个数
int Get_Length(PCNode plist);
//清空
void Clear(PCNode plist);
//销毁1
void Destroy1(PCNode plist);
//销毁2
void Destroy2(PCNode plist);
void Show(PCNode plist);
clist.cpp
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "clist.h"
//初始化函数(给头结点赋值)
void Init_clist(PCNode plist)
{
assert(plist != NULL);
if(plist == NULL)
{
return;
}
//plist->data; 头结点的数据域不使用
plist->next = plist;
}
//头插
bool Insert_Head(PCNode plist, int val)
{
assert(plist!=nullptr);
//1.创建一个新节点
CNode * pnewnode = (CNode*)malloc(sizeof(CNode) * 1);//动态内存申请一个新节点
assert(pnewnode != NULL);
pnewnode->data = val;
pnewnode->next = NULL;//此行代码 可加可不加 因为紧接着就会对pnewnode->next 重新赋值
//2.找到合适的插入位置(头插函数 这一步不需要处理)
//3.插入新节点 1和2 顺序不能颠倒
pnewnode->next = plist->next;//1
plist->next = pnewnode;//2
return true;
}
//尾插
bool Insert_Tail(PCNode plist, int val)
{
assert(plist!=nullptr);
//1.创建新节点
CNode* pnewnode = (CNode)malloc(sizeof(CNode) * 1);
assert(pnewnode != NULL);
pnewnode->data = val;
//2.先找到最后一个节点
CNode *p=plist;
for(p; p->next!=plist; p=p->next); //此时for循环结束 p指向的是尾结点
//3.给它后面插入
pnewnode->next = p->next;
p->next = pnewnode;
return true;
}
//按位置插 pos
bool Insert_pos(PCNode plist, int pos, int val)
{
assert(plist!=NULL && pos>=0 && pos<=Get_Length(plist));
if(plist == NULL || pos<0 || pos>Get_Length(plist))
return false;
//1.创建新节点
CNode *pnewnode = (CNode)malloc(sizeof(CNode) * 1);
assert(pnewnode != NULL);
pnewnode->data = val;
//2.找到合适的插入位置
CNode* p = plist;
for(int i=0; i<pos; i++)
{
p=p->next;
}
//此时 p就指向待插入位置的前驱
//3.给它后面插入
pnewnode->next = p->next;
p->next = pnewnode;
return true;
}
//头删
bool Del_Head(PCNode plist)
{
assert(plist!=nullptr);
if(IsEmpty(plist))
return false;
//1.申请一个临时变量指向要删除的第一个有效节点
CNode *p = plist->next;
//2.将要删除的节点左右连接起来
plist->next = p->next;//plist->next = plist->next->next;
//3.将要删除的节点内存释放,防止内存泄露
free(p);
p = NULL;
return true;
}
//尾删
bool Del_Tail(PNode plist)
{
assert(plist!=nullptr);
//1.先找到要删除节点的上一个节点(前驱)
CNode *p =plist;
for(p; p->next->next!=plist; p=p->next);
//此时p就指向倒数第二个节点
//2.申请一个临时变量指向要删除的有效节点
CNode *q = p->next;
//3.跨越指向(将要删除的节点左右连接起来)
p->next = q->next;//p->next = p->next->next;
//4.释放内存
free(q);
q=NULL;
return true;
}
//按位置删 pos
bool Del_Pos(PCNode plist, int pos)
{
assert(plist!=NULL && pos>=0 && pos<Get_Length(plist));
if(plist==NULL || pos<0 || pos>=Get_Length(plist))
return false;
//1.找到删除节点的上一个节点
CNode *p = plist;
for(int i=0; i<pos; i++)
{
p = p->next;
}
//此时p就指向pos指向节点的前一个节点
//2.申请一个临时变量指向要删除的有效节点
CNode *q = p->next;
//3.跨越指向(将要删除的节点左右连接起来)
p->next = q->next;
//4.释放内存
free(q);
return true;
}
//按值删(将值为val的第一个节点删除掉)
bool Del_val(PCNode plist, int val)
{
assert(plist!=nulptr);
CNode *p = Get_Prior(plist, val);
if(p==plist)
{
return false;
}
CNode *q = p->next;
p->next = q->next;
free(q);
q = NULL;
return true;
}
//查找 val 在plist这个单链表中找值为val的第一个节点 找到后返回其地址 不然返回NULL
struct CNode * Search(PCNode plist, int val)
{
//不需要前驱的操作 所以 用第二种for循环
assert(plist!=nullptr);
CNode *p = plist->next;
for(p; p!=plist; p=p->next)
{
if(p->data == val)
{
return p;
}
}
return NULL;
}
//判空
bool IsEmpty(PCNode plist)
{
return plist->next == plist;
}
//判满 单链表不需要判满
//获取前驱 (将值为val的节点去前一个节点返回)
struct CNode* Get_Prior(PCNode plist, int val)
{
assert(plist!=nullptr);
CNode *p=plist;
for(p; p->next!=plist; p=p->next)
{
if(p->next->data == val)
{
return p;
}
}
return NULL;
}
//获取后继 (将值为val的节点去后一个节点返回)
struct CNode* Get_Next(PCNode plist, int val)
{
CNode *p = Search(plist, val);
if(p==plist)
{
return NULL;
}
return p->next;
}
//获取其有效节点个数
int Get_Length(PCNode plist)
{
assert(plist!=nullptr);
int count = 0;
CNode *p = plist->next;
for(p; p!=plist; p=p->next)
{
count++;
}
return count;
}
//清空
void Clear(PCNode plist)
{
Destroy1(plist);
}
//销毁1 一致头删 这个一定掌握(只用了一个指针)
void Destroy1(PCNode plist)
{
assert(plist!=nullptr);
/*while(!IsEmpty(plist))
{
Del_Head(plist);
}*/
while(plist->next != plist)
{
CNode *p = plist->next;
plist->next = p->next;
free(p);
p = NULL;
}
}
//销毁2 不借助头节点(两个指针) 能理解就理解 理解不了记住第一种头删的方式即可
void Destroy2(PCNode plist)
{
assert(plist!=nullptr);
CNode *p = plist->next;
CNode *q;
while(p != plist)
{
q = p->next;
free(p);
p = q;
}
plist->next = plist;
}
void Show(PCNode plist)
{
assert(plist!=nullptr);
CNode *p = plist->next;
for(p; p!=plist; p=p->next)
{
printf("%d ", p->data);
}
printf("\n");
}
8.例题
1>单链表如何逆置
2>
(1)如何判断两个单链表是否存在交点
(2)找出相交首节点
3>删除p某节点(p某一定不能是尾节点)
4>如何判断一个单链表是否存在一个环,如果存在一个环,将入环点返回
#include"list.h"
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<math.h>
//1。单链表如何逆置
//一直头插
void Reverse1(PNode plist)
{
assert(plist!=nullptr);
Node* p=plist->next;
Node* q=nullptr;
plist->next=nullptr;
while(p!=plist)
{
q=p->next;
p->next=plist->next;
plist->next=p;
p=q;
}
}
//不借助头结点,用三个指针
void Reverse2(PNode plist)
{
assert(plist!=nullptr&&plist->next!=nullptr);
Node* p=plist->next;
Node* q=p->next;
Node* r=nullptr;
p->next=nullptr;
while(q!=nullptr)
{
r=q->next;
q->next=p;
p=q;
q=r;
}
plist->next=p;
}
//2.如何判断两个单链表是否存在交点 相交节点只有一个next域,也就是说相交节点后面只有一根线
bool Intersect(PNode plist1,PNode plist2)
{
assert(plist1!=nullptr&&plist2!=nullptr);
Node* p=plist1;
Node* q=plist2;
for(p;p->next!=nullptr;p=p->next);
for(q;q->next!=nullptr;q=q->next);
return p==q?true:false;
/*if(p==q)
{
return true;
}
return false;*/
}
//找出相交首节点
//让长的单链表先走几步,保证两条单链表长度相等,然后同步比较
Node* Intersect_get_first(PNode plist1,PNode plist2)
{
assert(plist1!=nullptr&&plist2!=nullptr);
int len1=Get_Length(plist1);
int len2=Get_Length(plist2);
Node* p=len1>=len2?plist1:plist2;
Node* q=len1>=len2?plist2:plist2;
for(int i=0;i<abs(len1-len2);i++)//ads()是取绝对值函数
{
p=p->next;
}
while(p!=nullptr)
{
if(p==q)
{
return p;//return q;
}
p=p>next;
q=q->next;
}
return NULL;
}
//3.删除p节点(p一定不能是尾节点)
bool Del_Node(PNode plist,PNode p)
{
assert(plist!=nullptr);
if(p->next==nullptr)
{
return Del_Tail(plist);
//return false;
}
p->data=p>next->data;
Node* q=p->next;
p->next=q->next;//换脸术//重要的数据,节点还可以再向系统申请
free(q);
return true;
}
//4.如何判断一个单链表是否存在一个环,如果存在一个环,将入环点返回
//追击问题
Node* IsCircle(PNode plist)
{
assert(plist!=nullptr&&plist->next!=nullptr);
Node* fast=plist->next->next;
Node* slow=plist->next;
while(fast!=nullptr&&fast!=slow)//左边没有环 右边代表有环且相遇
{
slow=slow->next;
fast=fast->next;
if(fast==nullptr)
{
return false;
}
fast=fast->next;
}
if(fast==nullptr)
{
return NULL;
}
//此时单链表有环
/*
头结点到入环点距离为x,入环点到相遇点距离为y,相遇点到入环点(剩下的弧形)距离为z
slow=x+y fast=x+y+n(y+z)
2(x+y)=x+y+n(y+z) => x=(n-1)(y+z)+z
让一个指针从头节点开始跑,让另一个指针以同样的速度从相遇点开始跑,最后相遇就是入环点
*/
Node* p=plist;
Node* q=fast;
while(p!=q)
{
p=p->next;
q=q->next;
}
return q;//入环点
//return p;
}