链表
链接表是线性表的链接储存表示。
特点:
每个元素(表项)由结点(Node)构成。
一个元素有8byte;
4byte的数据,4byte的指针(存放下一个节点的地址)
4byte date | link 4byte
线性结构 :
结点可以连续,可以不连续存储
结点的逻辑顺序与物理顺序可以不一致
表可扩充
头文件
#ifndef LINKLIST_H
#define LINKLIST_H
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define SUCCESS 10000
#define FAILURE 10001
typedef int ElemType; //把int整形名字改为ElemType(主要是元素) 方便书写
struct Node
{
ElemType data; //数据域
struct Node *next; //定义一个指针指向结构体struct Node(指针域)
};
typedef struct Node node; //改名字,把struct Node 改为 node。方便书写
typedef struct Node *pNode; //把struct Node * 改为pNode (它是一个指针)
int InitLink(pNode *h);
int InsertLink(pNode h, int p, int num);
int TraverseLink(pNode h);
int GetElem(pNode h, int p);
int LocateElem(pNode h, int e);
int LengthLink(pNode h);
int PriorElem(pNode h, int num);
int NextElem (pNode h, int num);
void ReverseLink(pNode h);
int LinkDel (pNode h, int p);
int ClearLink(pNode h);
int DestoryLink(pNode *p);
#end
子函数,调用函数文件
#include "LinkList.h" //头文件(自己建立的链表的)
/*
函数描述: 链表初始化
函数参数: 链表头指针的地址
函数返回值: 成功返回SUCCESS,失败返回FAILURE;
*/
int InitLink(pNode *h)
{
if(NULL == h) //入参判断
{
return FAILURE;
}
*h = malloc(sizeof(node) * 1);
if(NULL == *h)
{
return FAILURE;
}
(*h) -> next = NULL; //定义好头节点, 把下一个节点定义为空。
return SUCCESS;
}
/*
函数描述:链表指定位置插入数据
函数参数:链表头指针 插入的位置 插入的数据
函数返回值: 成功返回SUCCESS 失败返回FAILURE
*/
int InsertLink(pNode h, int p, int num)
{
if(NULL == h) //入参判断
{
return FAILURE;
}
pNode t = h; //把头节点的地址给 t
int k = 1; //定义指针移动次数
while(k < p && t != NULL) //把指针移动到要插入位置的前一个位置
{
t = t -> next;
k++;
}
if(t == NULL || k > p) //p的值太大 或者 p的值太小
{
return FAILURE;
}
pNode n = (pNode)malloc(sizeof(node) * 1); //为链表插入的元素申请空间。
if(NULL == n)
{
return FAILURE;
}
n -> data = num; //把数据的值传给新元素的数据区域
n -> next = t -> next; //把原来前一个的元素的指针域的地址赋予给n的指针域。
t -> next = n; //把n的地址赋予给原来前一个元素的指针域,使其指向n这块内存。
return SUCCESS;
}
/*
函数描述: 遍历链表,打印出其中的元素
函数参数: 链表头指针
函数返回值: 失败返回FAILURE, 成功返回SUCCESS
*/
int TraverseLink (pNode h)
{
if(NULL == h)
{
return FAILURE;
}
pNodet t = h -> next; //指向第一个结点
while(t) // (t != NULL)
{
printf("%d ",t -> data);
t = t -> next;
}
printf("\n");
return SUCCESS;
}
\*
函数描述: 根据位置查找元素
函数参数: 链表头指针 输入的位置
函数返回值: 失败返回FAILURE,找到返回这个位置的元素
*\
int GetElem(pNode h, int p)
{
if(NULL == h)
return FAILURE;
if(p <= 0) //p太小
return FAILURE;
int k = 0;
pNode t = h; //从头节点开始
while ( k < p && t !=NULL) //把指针移动到要查找位置的前一个位置
{
t = t -> next;
k++;
}
if(!t) // if(t) == if(t != NULL) if(!t) === if(t == NULL) 防止p值过大
{
return FAILURE;
}
return t -> data;
}
/*
函数描述: 根据元素查找位置
函数参数: 链表头指针 输入的元素
函数返回值:找不到返回失败FAILURE,查找成功返回这个元素的位置
*/
int LocateElem(pNode h, int e)
{
if(NULL == h)
return FAILURE;
pNode t = h -> next; //从第一个节点开始
int i =1;
while(t)
{
if(t -> data == e)
{
return i;
}
i++;
t = t -> next;
}
return FAILURE;
}
/*
函数描述: 求出链表的长度
函数参数: 链表头指针
函数返回值: 失败返回FAILURE,成功返回链表的长度
*/
int LengthLink(pNode h)
{
if(NULL == h)
return FAILURE;
// pNode t = h; //从头节点开始
pNode t = h -> next; //从第一个节点开始
int i = 0;
while (t)
{
t = t -> next;
i++;
}
//return i - 1; //从头节点开始。
return i; //按道理来说 i++后输入的值为 i +1,所以要减一。
但这里因为前面 i = 0,按道理 t指向第一个结点,i应该等于 1。(这里是简化了)
}
/*
函数描述:查找元素的前驱
函数参数:链表头指针 输入的元素
函数返回值: 找不到返回FAILURE,找到返回这个元素的前驱
*/
int PriorElem(pNode h, int num)
{
if(NULL== h)
return FAILURE;
pNode t =h -> next; //指向第一个节点
while(t -> next) // t -> next != NULL
{
if(num == t -> next -> data)
//依次比较输入的元素和链表里的元素 因为链表和顺序表不一样,链表是通过指针传输地址,不像顺序表是连续的空间,位置变量能随意前进或者后退。而链表是用指针传输地址,其中指针是位置变量,但指针不能返回。
eg: 数组[i] i是位置,可以进行加减。而指针 *p指向的是一个地址,地址不能加减。而每个链表元素都有一个自己的地址和指针域的下一个元素的地址,只能通过指针p取访问,因此链表的位置变量只能前进,不能后退。
所以要想找到元素的前驱,只能通过前一个元素的指针域取访问下一个元素的数据域,如果相等,则这个元素就是输入元素的前驱,返回这个元素的数据域即可。简单一点说就是找到这个元素的前一个位置,通过指针域去判断元素是否相等。
{
return t -> data;
}
t = t -> next;
} //因为前面已经指向第一个节点了,所以循环里面是从第二个开始判断的。
return FAILURE;
}
/*
函数描述: 查找元素后继节点
函数参数: 链表头指针 输入的元素
函数返回值:找不到返回失败FAILURE,找到返回这个元素的后继节点
*/
int NextElem(pNode h, int num)
{
if(NULL == h)
return FAILURE;
pNode t = h -> next;
while(t -> next)
{
if(num == t -> data)
// 因为是查找后继节点,所以可直接找到元素所在位置,它的下一个元素就是后继节点。因此,直接依次比较输入的元素和链表里的元素、如果相等,说明下一个元素及时后继节点。
{
return t -> next ->data;
} //
t = t ->next;
}
return FAILURE;
}
/*函数描述:链表翻转,颠倒顺序
函数参数:链表头指针
函数返回值: 无
*/
void ReverseLink(pNode h)
{
if(NULL == h)
return ;
pNode p = h -> next; // 把第一个结点的地址给 p
h -> next = NULL; // 把头结点的指针域赋值为空指针
while(p)
{
pNode q = p; //把p的地址给q
p = p -> next; //把 指针p向前移动,指向下一个结点。把下一个结点的地址赋予p
q -> next = h -> next; // 把现在的 h -> next的地址赋予q -> next
h -> next = q; //把现在p的地址赋予h -> next
}
}
/*
函数描述:删除指定位置的元素
函数参数: 链表头指针 输入的位置
函数返回值:删除失败返回FAILURE , 删除成功返回删除的元素
*/
int LinkDel(pNode h, int p)
{
if(NULL == h)
return FAILURE;
pNode t = h;
int k = 1; 指针移动的次数
while(k < p && t != NULL) //把指针移动到要删除位置的前一个位置
{
t = t ->next;
k++;
}
if(t == NULL || k > p || !(t -> next)) // p的值过大或着p的值太小
{
return FAILURE;
} //无法删除最后一个结点
pNode n = t -> next; // 要删除的结点
int e = n -> data;
t -> next = n -> next;
free(n); //释放链表元素申请的空间
return e;
}
/*
函数描述:清空链表
函数参数: 链表的头指针
函数返回值:失败返回FAILURE,成功返回SUCCESS
*/
int ClearLink(pNode h)
{
if(NULL == h)
return FAILURE;
pNode t =h -> next;
while(t)
{
h -> next = t -> next; //把第一个节点的指针域给h的指针域
free(t); //释放第一个结点的空间。
t = h ->text ; //指针移向下一个结点
}
return SUCCESS;
}
/*
函数描述:清除链表
函数参数:链表头指针的地址
函数返回值:失败返回FAILURE,成功返回SUCCESS
*/
int DestoryLink(pNode *p)
{
if(NULL == h)
return FAILURE;
free(*h); 释放链表头指针本身的地址空间
*h == NULL;//让头指针中包含的地址为空(赋值为空指针)
return SUCCESS;
}
主函数文件
#include "LinkList.h"
int main()
{
srand (time (NULL)); //随机时间随机数字函数
pNode head = NULL; //定义链表的头指针
int ret ;
//链表初始化
ret = InitLink(&head);
if(SUCCESS == ret)
{
printf("链表初始化成功\n");
}
else
{
printf("链表初始化失败!\n");
}
//指定位置插入元素(这里是去全插入,相当于把数据传给链表)
int i, num;
for (i =0; i < 7; i++)
{
num = rand () %10;
ret = InsertLink(head, i+1, num); // 尾插法
// ret = InsertLink(head, 1, num); //头插法
if (FAILURE == ret)
{
printf("插入元素%d失败\n". num);
}
else
{
printf("插入元素%d成功\n", num);
}
}
// 根据位置查找元素
int p = 6;
ret = GetElem(head, p); //获取指定元素的位置
if (FAILURE == ret)
{
printf("第%d个元素不存在\n", p);
}
else
{
printf("第%d个元素是%d\n", p, ret);
}
//根据元素查找位置
num = 6;
ret = LocateElem(head, num); //返回指定元素的位置
if(FAILURE == ret)
{
printf("元素%d不存在\n", num);
}
else
{
printf("元素%d是第%d个元素\n", num);
}
printf("链表的长度是%d\n", LengthLink(head));
//根据元素查找元素的前驱
num = 6;
ret = PriorElem (head, num); //返回指定元素的前驱
if(FAILURE == ret)
{
printf("%d没有前驱\n", num);
}
else
{
printf("%d的前驱是%d\n", num, ret);
}
//根据元素查找元素的后继结点
num = 6;
ret = NextElem(head, num);
if(FAILURE == ret)
{
printf("%d没有后继结点\n", num);
}
else
{
printf("%d的后继结点是%d\n", num, ret);
}
//删除指定位置的元素
for(i = 0; i < 3; i++)
{
p = rand() %4; //随机产生一个位置
ret = LinkDel(head, p);
if(FAILURE == ret)
{
printf("删除第%d个元素失败\n", p);
}
else
{
prinntf("删除第%d个结点成功,元素是%d\n", p , ret);
}
}
//清空链表
ret = ClearLink(head);
if(FAILURE == ret)
{
printf("清空链表失败\n");
}
else
{
printf("清空链表成功\n");
}
ret = DestoryLink(&head);
if(FAILURE == ret)
{
printf("清除失败\n");
}
else
{
printf("清除成功\n");
}
return 0;
}