线性表的链式存储结构特点使用一组任意的存储单元存储线性表的数据元素(这组存储单元地址可以是连续的,也可以是不连续的)。因此,为了表示每个数据元素与其前后的数据元素的逻辑关系,除了需要存储本身的数据信息之外,还需要存储一个指针来指向当前位置的下一个数据元素的位置。于是需要定义一个数据类型,里面包含两个域,数据域和指针域,我们称之为结点。
其数据类型(结点)定义方式如下:
typedef struct LNode
{
int data; //数据域
struct LNode *next; //指针域
}LNode;
我们需要做的就是对上面这个结点进行一系列的操作(增,删,改,查,等)。
这里我们要特别分清楚头指针、头结点、首元结点这三个概念。
头指针:他指向表中的第一个节点,由它的起始位置来构造这一串链表,每次操作链表都需要找到头指针才能找到对应的链表。所以头指针是必不可少的。
头结点:它的位置在头指针之后,第一个储存数据元素结点(图中a1的位置)之前。头结点的数据域可以不存储任何的数据,也可以存储单链表当前的长度之类的信息(以下的代码就是),他的指针域指向第一个结点(a1)。一个单链表中可以没有头结点,但是不可以没有头指针。设置头结点的作用是插入(第一个结点之前)和删除首元结点时不用进行特殊处理。
首元结点:第一个存储数据元素的结点(图中的a1)。
下面是创建有头结点和无头结点链表的代码,对比一下。
有头结点:
LinkList *CreateList_L(int n) //创建
{
int i;
LNode *p;
LinkList *L = (LinkList *)malloc(sizeof(LinkList)); //头指针
L -> size = 0;
L->head = (LNode *)malloc(sizeof(LNode)); //头结点
L->head->data = 0;
L->head->next = NULL;
return L;
}
无头结点:
LNode *CreateNoheadList_L(int n)
{
int i;
LNode *q;
LNode *L = (LNode *)malloc(sizeof(LNode));
L->data = n;
L->next = NULL;
return L;
}
下面是完整的单链表创建及操作的完整代码:
头文件#include"Link_List.h"
#ifndef LINK_LIST_H_INCLUDED
#define LINK_LIST_H_INCLUDED
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct LNode
{
int data;
struct LNode *next;
}LNode;
typedef struct LinkList
{
LNode *head;
int size;
}LinkList;
LinkList *CreateList_L(int n) //创建
{
int i;
LNode *p;
LinkList *L = (LinkList *)malloc(sizeof(LinkList));
L -> size = 0;
L->head = (LNode *)malloc(sizeof(LNode));
L->head->data = 0;
L->head->next = NULL;
for( i = 0;i < n;i++)
{
p = (LNode *)malloc(sizeof(LNode));
scanf("%d",&p -> data);
p -> next = L -> head-> next;
L -> head -> next = p;
}
L->size = n;
printf("链表创建成功!\n");
return L;
}
void DestoryList_L(LinkList *L) //销毁
{
if(L == NULL)
return;
LNode *current = L->head;
LNode *Next;
while( current != NULL)
{
Next = current ->next;
free(current);
current = Next;
}
L ->size = 0;
free(L);
printf("链表销毁成功!\n");
}
void InsertList_L(LinkList *L, int pos, int e) //插入元素
{
if( L == NULL )
return;
if( pos < 0 || pos > L->size)
{
pos = L->size;
}
LNode *newnode = (LNode *)malloc(sizeof(LNode));
newnode->data = e;
newnode->next = NULL;
LNode *current = L->head;
int i;
for( i = 0; i < pos; i++ )
{
current = current ->next;
}
newnode ->next = current ->next;
current ->next = newnode;
L ->size++;
}
void DeleteListByPos_L(LinkList *L, int pos) //按位置进行删除元素
{
if( L == NULL)
return;
if( pos < 0 || pos > L->size )
{
printf("你输入的位置不合法,请重新输入!\n");
return;
}
int i;
LNode *Next;
LNode *current = L->head;
for( i = 0; i < pos; i++ )
{
current = current ->next;
}
Next = current ->next;
current ->next = current ->next->next;
free(Next); //后释放结点
L->size--;
}
void DeleteListByValue_L(LinkList *L, char e)
{
if( L == NULL)
return;
int i = 0 ;
LNode *current = L->head;
while(current ->next->data != e)
{
current = current ->next;
i++;
if(i >= L->size)
{
printf("您要删除的元素不存在,请重新输入!");
break;
}
}
LNode *Next = current ->next;
current->next = current ->next->next;
free(Next);
L->size--;
}
int FindList_L(LinkList *L,char e) //查找
{
int i = 1;
LNode *current = L->head->next;
while( current != NULL )
{
if(current ->data == e) break;
if( i >= L->size)
{
printf("您要查找的元素不存在,请重新输入!");
return -1;
}
current = current->next;
i++;
}
printf("您要查找的元素位于第%d位!\n",i);
return i;
}
void PrintList_L(LinkList *L) //打印
{
if(L == NULL)
{
printf("链表为空!");
return;
}
LNode *current = L->head->next;
while( current != NULL)
{
printf("%d\t",current -> data);
current = current -> next;
}
printf("\n");
}
int SizeList_L(LinkList *L)
{
return L->size;
}
//删除表中所有值大于mink且效益maxk的元素,同时释放删除节点的空间
//当时调试错误在于没有明确指针的位置,利用画图来帮助理解
void DeleteListBetween(LinkList *L,int mink,int maxk)
{
if( L == NULL)
{
printf("链表为空!");
return;
}
LNode *current = L->head;
LNode *Next;
while(current ->next != NULL)
{
if( current->next->data > mink && current->next->data < maxk)
{
Next = current->next;
current ->next = current ->next->next;
free(Next);
L->size--;
//current = current ->next;
}
else
{
current = current->next;
}
}
printf("删除并释放节点成功!\n");
}
//删除表中所有值相同的多余元素
void DeleteListSameNode(LinkList *L)
{
if( L == NULL)
{
printf("链表为空!\n");
return;
}
LNode *current = L->head;
LNode *Next,*pre;
while(current ->next != NULL)
{
pre = current;
if(current->next->data == pre->data)
{
Next = current->next;
current ->next = current ->next->next;
free(Next);
L->size--;
}
else
{
current = current ->next;
}
}
printf("删除相同的值成功!\n");
}
//将链表逆置
void ReverseList_L(LinkList *L)
{
if( L == NULL)
{
printf("链表为空!\n");
return;
}
LNode *current = L ->head->next;
LNode *Next;
L ->head->next = NULL;
while(current != NULL)
{
Next = current;
current = current->next;
Next->next = L->head->next;
L->head->next = Next;
}
printf("链表逆置成功!\n");
}
//合并两个单链表并用La或Lb作为存储空间,插入位置是La链表的尾端
void MergeList_L(LinkList *La, LinkList *Lb)
{
//int size = 0;
if(La == NULL || Lb == NULL)
{
printf("其中有链表为空,不需要合并!");
return;
}
LNode *pa = La->head,*pb = Lb->head;
while(pa->next!= NULL)
{
if(pa->next != NULL)
pa = pa->next;
}
while(pb->next != NULL)
pb = pb->next;
pb->next = pa->next;
pa->next = Lb->head->next;
La->size = La->size + Lb->size;
//DestoryList_L(Lb);
//printf("链表合并完成,当前链表的长度为%d:\n",size);
}
#endif // LINK_LIST_H_INCLUDED
头文件#include"NoHeadLink_List.h"
#ifndef NOHEADLINK_LIST_H_INCLUDED
#define NOHEADLINK_LIST_H_INCLUDED
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "Link_List.h"
LNode *CreateNoheadList_L(int n)
{
int i;
LNode *q;
LNode *L = (LNode *)malloc(sizeof(LNode));
L->data = n;
L->next = NULL;
q = L;
for( i = 0; i < n;i++ )
{
LNode *p = (LNode *)malloc(sizeof(LNode));
scanf("%d",&p->data);
q->next = p;
p->next = NULL;
q = p;
}
printf("无头链表创建成功!\n");
return L;
}
void PrintNoheadList_L(LNode *L)
{
if( L == NULL)
{
printf("此链表为空!");
return;
}
LNode *current = L ->next;
while(current != NULL)
{
printf("%d\t",current->data);
current = current ->next;
}
printf("\n无头链表输出成功!\n");
}
int NoheadSize(LNode *L)
{
return L->data;
}
#endif // NOHEADLINK_LIST_H_INCLUDED
主函数main
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"Link_List.h"
#include"NoHeadLink_List.h"
void testMerge() //测试合并
{
LinkList *La = CreateList_L(5);
PrintList_L(La);
printf("----------------------------------------\n ");
LinkList *Lb = CreateList_L(5);
PrintList_L(Lb);
MergeList_L(La,Lb);
printf("=================合并之后的链表=========================\n\n");
PrintList_L(La);
printf("\n当前链表的长度为:%d\n",SizeList_L(La));
}
void testRevrese() //测试逆转
{
LinkList *L = CreateList_L(5);
printf("----------逆转前---------\n");
PrintList_L(L);
printf("\n当前链表的长度为:%d\n",SizeList_L(L));
ReverseList_L(L);
printf("----------逆转后---------\n");
PrintList_L(L);
printf("\n当前链表的长度为:%d\n",SizeList_L(L));
}
void testSameNode() //测试删除相同的结点并进行释放
{
LinkList *L = CreateList_L(5);
printf("----------删除前---------\n");
PrintList_L(L);
printf("\n当前链表的长度为:%d\n",SizeList_L(L));
DeleteListSameNode(L);
printf("----------删除后---------\n");
PrintList_L(L);
printf("\n当前链表的长度为:%d\n",SizeList_L(L));
}
void testBetween() //测试删除中间的值
{
LinkList *L = CreateList_L(5);
printf("----------删除前---------\n");
PrintList_L(L);
DeleteListBetween(L,0,6);
printf("----------删除后---------\n");
PrintList_L(L);
printf("\n当前链表的长度为:%d\n",SizeList_L(L));
}
void testwWT() //测试无头链表
{
LNode *L = CreateNoheadList_L(5);
PrintNoheadList_L(L);
printf("当前无头链表的长度为:%d\n",NoheadSize(L));
}
void test() //测试带头链表
{
LinkList *L = CreateList_L(5);
PrintList_L(L);
printf("---------插入后-------------\n");
InsertList_L(L,3,6);
PrintList_L(L);
InsertList_L(L,2,1);
PrintList_L(L);
system("pause");
printf("---------按值删除后-------------\n");
DeleteListByValue_L(L,1);
PrintList_L(L);
printf("---------按位置删除后-------------\n");
DeleteListByPos_L(L,2);
PrintList_L(L);
printf("---------查找结果-------------\n");
FindList_L(L,5);
DestoryList_L(L);
printf("\n当前链表的长度为:%d\n",SizeList_L(L));
}
int main()
{
testMerge();
//testRevrese();
//testSameNode();
//testBetween();
//testwWT();
//test();
system("pause");
return 0;
}
注:部分代码在程序测试时为了方便其他函数进行测试已经注释掉,如需参考请略加修改。