链表的概念

(2)头指针:如上图所示,链表中第一个结点的存储位置叫做头指针。
(3)头结点:头结点是放在第一个元素结点之前的结点,头结点不是链表中的必须元素,其数据域一般无意义,(有些情况下会存放链表的长度,或用作监视哨等),本文的头结点存放了链表的长度。有了头结点后,对在第一个元素结点前插入结点和删除第一个结点,其操作与对其它结点的操作统一了。
(4)首元结点:就是第一个元素的结点,它是头结点后边的第一个结点。
单链表的基本操作
(1)初始化:给链表添加一个头结点。
(2)逆置链表:将单链表所有元素逆置。
(3)链表遍历:打印出链表的长度,并按顺序打印所有元素。
(4)查找:获取指定位置的元素。
(5)元素插入:在指定位置插入元素。
(6)元素删除:删除指定位置的元素。
(7)删除链表:将链表删除。
(8)头插法创建链表:用头插法插入一系列元素,创建链表。
(9)尾插法创建链表:用尾插法插入一系列元素,创建链表。
(10)快慢指针:利用快慢指针法的原理,快速查找一个长度未知的链表的中间结点。
这些操作中,链表逆置具有一定的难度,其基本的设计思想是:将以前的旧链表中的每个元素从旧链表中删除,并用头插法插入新的链表中。
快慢指针的设计也非常巧妙,快指针运动速度是慢指针的两倍,因此快指针指向最后一个结点的时候,慢指针刚好指向中间结点。
线性表的链式存储结构的特点
用一组任意的存储单元存储线性表的数据元素(单元可以是连续的,也可以是不连续的)
结点(node)
结点包括两个域
**数据域(data)**存储数据元素信息的域
**指针域(next)**存储直接后继存储位置的域,指针域中存储的信息称为指针或链
单链表编程实现----C语言
在C语言中函数和数据是分开的,每个函数都需要考虑链表的数据传递问题,这大大加大了编程的难度。 (1)链表的头文件 linklist.h
#ifndef LINKLIST_H
#define LINKLIST_H
typedef int ElemType;
typedef struct Node
{
ElemType data;
struct Node *Next;
}Node;
typedef Node* linklist;//给节点指针取别名
typedef enum Bool
{
FALSE,TRUE//枚举默认值从0开始,依次加1
}Bool;
Bool InitList(linklist* L);//初始化链表,给链表添加一个头结点
Bool GetElem(linklist L, int i, ElemType* e);//获得第i个位置的元素
Bool ListInsert(linklist* L,int i,ElemType e);//在第i个位置插入元素
Bool ListDelete(linklist* L,int i,ElemType* e);//删除第i个元素
Bool ListDeleteAll(linklist* L);//删除全部链表
void traverseList(linklist L);//遍历链表
void CreateListHead(linklist* L,int n);//头插法创建链表
void CreateListEnd(linklist* L,int n);//尾插法创建链表
void Reverse(linklist* L);//逆置链表
int findMiddle(linklist L, ElemType* e);//查找中间结点,将查找的元素值赋给e,并返回该节点是第几个结点,如果是空表则返回0
#endif // LINKLIST_H
(2)链表的源文件linklist.c
#include "linklist.h"
#include <stdio.h>
Bool GetElem(linklist L, int i, ElemType* e)
{
linklist p=L;
int j;
for(j=1;j<=i;j++)
{
p=p->Next;
if(p==NULL)
return FALSE;
}
*e=p->data;
return TRUE;
}
Bool ListInsert(linklist* L,int i,ElemType e)
{
linklist p=*L,s;
int j;
for(j=1;j<i;j++)
{
p=p->Next;
if(p==NULL)
return FALSE;
}
s=(linklist)malloc(sizeof(Node));
s->data=e;
s->Next=p->Next;
p->Next=s;
(*L)->data++;
return TRUE;
}
Bool ListDelete(linklist* L,int i,ElemType* e)
{
linklist p=*L,q;
int j;
for(j=1;j<i;j++)
{
p=p->Next;
if(p==NULL)
return FALSE;
}
q=p->Next;
p->Next=q->Next;
(*L)->data--;
*e=q->data;
free(q);
return TRUE;
}
Bool InitList(linklist* L)
{
*L=(linklist)malloc(sizeof(Node));
(*L)->data=0;
(*L)->Next=NULL;
return TRUE;
}
void traverseList(linklist L)
{
linklist p=L;
printf("链表总长度为:%d \n",p->data);
p=p->Next;
while(p)
{
printf("%d ",p->data);
p=p->Next;
}
printf("\n\n");
// printf("display ok!\n");
}
void CreateListHead(linklist* L,int n)
{
linklist p;
int i;
for(i=0;i<n;i++)
{
p=(linklist)malloc(sizeof(Node));
p->data=i;//rand()%100+1;
p->Next=(*L)->Next;
(*L)->Next=p;
(*L)->data++;
}
}
void CreateListEnd(linklist* L,int n)
{
linklist p,end=*L;
int i;
for(i=0;i<n;i++)
{
p=(linklist)malloc(sizeof(Node));
p->data=i;//rand()%100+1;
p->Next=NULL;
end->Next=p;
end=p;
(*L)->data++;
}
}
Bool ListDeleteAll(linklist* L)
{
linklist p=*L,q;
while(p->Next)
{
q=p->Next;
p->Next=q->Next;
(*L)->data--;
free(q);
}
free(p);
return TRUE;
}
void Reverse(linklist* L)//翻转链表
{
linklist cur, tmp;
//初始化新的头结点
cur=*L;
cur=cur->Next;//旧链表的头结点被删除(赋给了新的头结点)
(*L)->Next=NULL;//注意这一句和上一句不能颠倒,不然cur无法向后移
while (cur)
{
tmp=cur;
cur=cur->Next;//顺序删旧链表的元素
tmp->Next=(*L)->Next;//被删掉的元素用头插法插入新的头结点
(*L)->Next=tmp;
}
}
int findMiddle(linklist L, ElemType* e)
{
linklist fast,slow;
int i;
fast=slow=L;
for(i=0;fast!=NULL&&fast->Next!=NULL;fast=fast->Next->Next)
{
slow=slow->Next;
i++;
}
*e=slow->data;
return i;
}
(3) 主程序main.c
#include <stdio.h>
#include "linklist.h"
int main()
{
ElemType a;
linklist list;
int num;
printf(" 1.初始化\n");
InitList(&list);
traverseList(list);
InitList(&list);
printf(" 2.尾插法新建单链表\n");
CreateListEnd(&list,10);
traverseList(list);
ListDeleteAll(&list);
InitList(&list);
printf(" 3.头插法新建单链表\n");
CreateListHead(&list,10);
traverseList(list);
printf(" 4.元素插入\n");
ListInsert(&list,5,10);
traverseList(list);
printf(" 5.元素删除\n");
ListDelete(&list,5,&a);
printf("被删除的元素为:%d ",a);
traverseList(list);
printf(" 6.元素查找\n");
GetElem(list,3,&a);
printf("第3个元素为:%d \n\n\n",a);
printf(" 7.逆置链表\n");
printf("原链表----------");
traverseList(list);
Reverse(&list);
printf("逆置链表---------");
traverseList(list);
printf("\n\n 8.快慢指针查找中间结点\n");
printf("原链表\n");
traverseList(list);
num=findMiddle(list,&a);
printf("中间结点为%d,这是链表的第%d个结点\n\n\n",a,num);
ListDeleteAll(&list);
return 0;
}
###单链表编程实现----C++ (1)链表类的声明 linklist.h
#ifndef LIST_H
#define LIST_H
template<typename ElemType>//typedef int ElemType;
class List
{
public:
struct Node
{
ElemType data;
struct Node *next;
};
List();//链表,可以删除所有动态节点
~List();//删除全部节点
void traverseList();//遍历
bool ListInsert(int i,ElemType d);//在第i个节点位置插入元素d
void CreateListHead(int n);//头插法插入
void CreateListEnd(int n);//尾插法插入
void Reverse();//逆置单链表
bool ListDelete(int i,ElemType& e);//删除第i个元素
bool GetElem(int i,ElemType& e);//获取第i个节点位置的元素,并且返回给e
int findMiddle(ElemType& e);//查找中间结点,将查找的元素值赋给e,并返回该节点是第几个结点,如果是空表则返回0
private:
Node *head;//存放头结点
};
#endif // LIST_H
(2)链表类的实现linklist.cpp
#include "linklist.h"
#include<iostream>
using namespace std;
template<typename ElemType>
List<ElemType>::List()//初始化
{
head=new Node;
head->next=NULL;
head->data=0;//头指针的数据域也不要浪费:用来存放链表长度
}
template<typename ElemType>
List<ElemType>::~List()//删除
{
Node* del;
while (head->next!=NULL)
{
del=head;
head=head->next;
delete del;
}
delete head;
}
template<typename ElemType>
void List<ElemType>::traverseList()//遍历
{
Node *ph=head;
cout<<"链表总长度为:"<<head->data<<endl;
ph=ph->next;
for(;ph!=NULL;ph=ph->next)
cout<<ph->data<<" ";
cout<<endl;
}
template<typename ElemType>
void List<ElemType>::CreateListHead(int n)//头指针插入
//头插法:
//head->NULL;
//head->p1->NULL;
//head->p2->p1->NULL;
{
for(int i=0;i<n;i++)
{
Node* cur=new Node;
cur->data=i;
cur->next=head->next;
head->next=cur;
head->data++;
}
}
template<typename ElemType>
bool List<ElemType>::ListInsert(int i,ElemType d)
{
Node *p=head,*cur;
for(int j=1;j<i;j++)
{
p=p->next;
if(p==NULL)
return false;
}
cur=new Node;
cur->data=d;
cur->next=p->next;
p->next=cur;
head->data++;
return true;
}
template<typename ElemType>
void List<ElemType>::CreateListEnd(int n)//末尾插入
//尾插法:
//head->NULL;
//head->p1->NULL;
//head->p1->p2->NULL;
{
Node* cur,*end;
end=head;
for(int i=0;i<n;i++)
{
cur=new Node;
cur->data=i;
cur->next=end->next;
end->next=cur;
end=cur;
head->data++;
}
}
template<typename ElemType>
bool List<ElemType>::ListDelete(int i,ElemType& e)
{
Node *p=head,*cur;
for(int j=1;j<i;j++)
{
p=p->next;
if(p==NULL)
return false;
}
cur=p->next;//待删除节点
e=cur->data;
p->next=cur->next;//将待删除节点的后一个节点赋给其前一个节点
head->data--;
delete cur;
return true;
}
template<typename ElemType>
bool List<ElemType>::GetElem(int i,ElemType& e)
{
Node* p=head;
for(int j=0;j<i;j++)
{
p=p->next;
if(p==NULL)
return false;
}
e=p->data;
return true;
}
/*
*这个程序是原始版本的程序,这个程序的思路更加清晰,下面一个程序是改进的程序,代码更加简洁。
*/
/*
void List<ElemType>::Reverse()//翻转链表
{
Node* cur=head;//头插法
Node* newhead,*tmp;
//初始化新的头结点
newhead=head;
cur=cur->next;//旧链表的头结点被删除(赋给了新的头结点)
newhead->next=NULL;//注意这一句和上一句不能颠倒,不然cur无法向后移
while (cur)
{
tmp=cur;
cur=cur->next;//顺序删旧链表的元素
tmp->next=newhead->next;//被删掉的元素用头插法插入新的头结点
newhead->next=tmp;
}
head= newhead;//这一步是多余的,因为新头和旧头都是指向同一位置,其实newhead是一个多余变量,直接用head也是可以完成任务的。
}
*/
template<typename ElemType>
void List<ElemType>::Reverse()//翻转链表
{
Node* cur,* tmp;//头插法
//初始化新的头结点
cur=head;
cur=cur->next;//旧链表的头结点被删除(赋给了新的头结点)
head->next=NULL;//注意这一句和上一句不能颠倒,不然cur无法向后移
while (cur)
{
tmp=cur;
cur=cur->next;//顺序删旧链表的元素
tmp->next=head->next;//被删掉的元素用头插法插入新的头结点
head->next=tmp;
}
}
template<typename ElemType>
int List<ElemType>::findMiddle(ElemType& e)
{
Node *fast,*slow;
int i;
fast=slow=head;
for(i=0;fast!=NULL&&fast->next!=NULL;fast=fast->next->next)
{
slow=slow->next;
i++;
}
e=slow->data;
return i;
}
(3)主程序main.cpp
#include <iostream>
#include"linklist.cpp"
using namespace std;
int main()
{
List<int> list;
bool success;
cout<<" 1.初始化"<<endl;
list.traverseList();
List<int> list1;
list1.CreateListEnd(10);
cout<<endl<<endl<<" 2.尾插法新建单链表 "<<endl;
list1.traverseList();
List<int> list2;
cout<<endl<<endl<<" 3.头插法新建单链表 "<<endl;
list2.CreateListHead(10);
list2.traverseList();
cout<<endl<<endl<<" 4.元素插入"<<endl;
list2.ListInsert(5,10);
list2.traverseList();
cout<<endl<<endl<<" 5.元素删除"<<endl;
int a;
list2.ListDelete(5,a);
cout<<"被删除的元素为:"<<a<<" ";
list2.traverseList();
cout<<endl<<endl<<" 6.元素查找"<<endl;
list2.traverseList();
success=list2.GetElem(3,a);
if(!success)
cout<<"error"<<endl;
else
cout<<"被查找的元素为:"<<a<<endl;
cout<<endl<<endl<<" 7.逆置链表"<<endl;
cout<<"原链表"<<endl;
list2.traverseList();
list2.Reverse();
cout<<endl<<"逆置链表"<<endl;
list2.traverseList();
cout<<endl<<endl<<" 8.快慢指针查找中间结点"<<endl;
int num;
cout<<"原链表"<<endl;
list2.traverseList();
num=list2.findMiddle(a);
cout<<"中间结点为"<<a<<",这是链表的第"<<num<<"个结点"<<endl<<endl<<endl;
return 0;
}
###运行结果
