单链表的类型声明
struct Node; typedef struct Node* PtrToNode; typedef PtrToNode List //header ptr typedef PtrToNode Position //当前位置 List MakeEmpty(List L); //制作空链表 int IsEmpty(List L); //是否是空表 int IsLast(List L); //是否是最后一个 Position Find(ElementType x, List L); //找到x,返回地址 void Delete(ElementType x,List L); //删除x Position FindPrevious( ElementType x, List L); //找到x前一个元素,返回地址 void Insert(ElementType x,List L, Position P); //插入 void DeleteList (List L); //删除整个表 Position Header(List L); //返回头指针 Position First(List L); //返回第一个元素地址 Position Advance(Position P); //返回最后一个元素地址 struct Node { ElementType element; Position next; }
List MakeEmpty(List L)制作空链表
List MakeEmpty(List L)
{
L = new struct Node;
L->num=0;
L->next=nullptr;
return L;
}
动态为L(指向结构的指针)分配足够的内存空间,然后把next指针设定为指向nullptr(C++11)。最后return L。 使用时要以赋值形式使用。
void InputElement(List L) [自己额外添加的] 输入元素
void InputElem(List L)
{
cout<<"Enter number into list,(Ctr+Z to quit)\n";
int temp;
Position P,previous;
while(cin>>temp)
{
P = new struct Node;
P->num = temp;
P->next = NULL;
if(L->next==nullptr)
{
L->next = P;
previous = P;
}
previous->next = P;
previous = P;
}
}
手绘图解
int IsEmpty(List L) && int IsLast(Position P, List L)
int isEmpty(List L)
{
return L->next==nullptr;
}
int isLast(Position P,List L)
{
return P->next == nullptr;
}
即使不用到表头L,也要有一个它的参数
Position find(int x,List L)找到链表中相关元素地址
Position find(int x,List L)
{
Position temp;
temp=L->next;
while(temp!=nullptr&&temp->num!=x)
{
temp = temp->next;
}
return temp;
}
从first one开始,遍历链表,时间复杂度O(N)
Position FindPrevious(int x,List L) //找到元素的前一个位置
Position FindPrevious(int x,List L)
{
Position P;
P=L;
while(P->next!=nullptr&&P->next->num!=x)
P = P->next;
return P;
}
同上,时间复杂度O(N)
(重点) insert && delete
void Delete(int x,List L) //注意不能是小写开头哦,因为是key word
{
Position P,temp;
P = FindPrevious(x,L);
if(!isLast(P,L)) //如果P是最后,则没有相关元素
{
temp = P->next;
P->next = temp->next;
delete temp;
}
}
void insert(int x,List L,Position P)
{
Position temp;
temp = new struct Node ;
if(temp == NULL) //一般情况不会发生...
cerr<<"Out of space\n";
temp->num=x;
temp->next = P->next;
P->next = temp;
}
手绘图解
不过insert的第三个参数为
Position P
,链表不是数组,没有索引,所以比较麻烦。所以如果可以的话可以换成其他参数。
insert的时间复杂度为O(1),delete的时间复杂度为O(N),因为调用了findprevious().
void PrintList(List L) //索引到最后一位
void PrintList(List L)
{
if(L->next==nullptr)
{
cout<<"List empty\n";
return;
}
Position P;
P = L->next;
while(P->next!=nullptr)
{
cout<<P->num<<" ";
P = P->next;
}
cout<<P->num<<endl;
}
void DeleteList(List L) //清除链表
void DeleteList(List L)
{
Position P, temp;
if(L->next==nullptr) return; //如果是空链表,return。 很重要!
P=L->next;
L->next = nullptr;
while(P->next!=nullptr)
{
temp = P->next;
delete P;
P=temp;
}
delete P;
}
这里一定要添加
if(L->next == nullptr) return;
如果不添加而且其他代码不改的话,那么P就是nullptr,然后你还delete P(销毁了某处内存空间块)
在头部insert或者尾部insert
void InsertBack(int x,List L)
{
Position p,temp;
p = L;
while(p->next!=nullptr)
p = p->next;
temp = new struct Node;
temp->num = x;
temp->next = p->next;
p->next = temp;
}
//尾部insert就是索引到尾部,然后insert
void InsertFront(int x,List L)
{
Position p;
p = new struct Node;
p->num = x;
p->next = L->next;
L->next = p;
}
//头部更简单些
尾指针rear
可以创建一个rear尾指针指向最后一个位置,这样就不用从到索引到尾了。
Position Rear = new struct Node;
Position temp = L;
while(temp->next != nullptr)
temp = temp->next;
Rear-> next = temp;
链表的游标实现
链表的游标实现就是用数组来代替链表,数组有一个cursor(游标),cursor为int类型,然后数值就是下一个元素的position。因为游标实现是用数组来实现,所以有一些功能需要我们自己定义完成(比如new、delete、用int来代替指针)。其余的各个函数功能可能也有一些小修改,下面写几个function,体会一下就好
#define MAXSIZE 10
struct Node
{
int data;
int next;
} cursor[MAXSIZE];
typedef int List;
typedef int Position;
//初始化
void initialize(List& L)
{
for(int i=0;i<MAXSIZE-1;i)
cursor[i].next=i+1;
cursor[MAXSIZE-1].next = 0;
头结点的next为1,第一个结点next为2……
最后一个结点的next为0(既头结点,既null)
}
//new与delete 替代
Position allocate(void)
{
Position P;
p = cursor[0].next;
cursor[0].next = cursor[p].next;
return P;
}
头结点的next总是保存下一个可用结构的位置值
void Free(Position P)
{
cursor[p].next = cursor[0].next;
cursor[0].next = p;
}
// 插入
void Insert(int x,List L,Position P)
{
Position temp,i;
temp = allocate();
if(temp == 0)
{
cout<<"out of space!";
return ;
}
cursor[temp].data =x;
cursor[temp].next = cursor[p].next;
cursor[p].next = temp;
}
双向循环链表
双向循环链表跟单链表比起来,多的部分可能就是一个指向前续数的指针,以及特性:最末一个结点的next指向头结点,头结点的prior指向尾结点。
1.结点
struct Node
{
int num;
struct Node* next;
struct Node* prior;
};
2.空链表create
List MakeEmpty()
{
List L;
L = new struct Node;
L->next = nullptr;
L->prior = nullptr;
return L;
}
3.填充链表
void FullList(List L)
{
Position p,q;
int n;
cout<<"Enter numbers (CTRL+Z to quit)\n";
while(cin>>n){
p = new struct Node;
if(L->next == nullptr){
L->next = p;
p->next = L;
L->prior = p;
p->prior = L;
q = p;
q->num = n;
continue;
}
p->next =L;
p->prior=q;
q->next=p;
L->prior=p;
p->num = n;
q=p;
;}
}
填充链表比较麻烦,得分成两部分,已有结点和没有结点。不过还是挺好理解的。
- 插入结点
void Insert(int x,int y,List L) //数字x插入到y之前
{
Position temp =L;
Position p = new struct Node;
p->num = x;
while(temp->next->num!=y)
{
temp = temp->next;
}
p->next = temp->next;
p->prior = temp;
temp->next->prior = p;
temp->next = p;
}
- 打印和反向打印
void PrintList(List L)
{
Position p = L;
while(p->next!=L){
cout<<p->next->num<<" ";
p = p->next;
}
}
void reversePL(List L)
{
Position p = L;
while(p->prior!= L)
{
cout<<p->prior->num<<" ";
p = p->prior;
}
}
注意,此时判断是否终止的条件已经从判断是否指向nullptr变成是否回到头结点!!
链表
应用
1. 基数排序
原理:
手绘图解
首先将数组中的数字按个位数,然后分别装进桶链表中。一轮完毕后,更新array同时clear桶链表,开始new round。
根据个位数、十位数、百位数……的顺序装入桶中,相当于排序,如果下一轮的下一位数两个数相同,那么进入链表的顺序就是上一轮的排序,然后就比出大小。
ps. insert使用insertback函数; 将链表集合到头文件中
ps2.0 : 排序时严格按照此顺序:创建链表、根据digit插入链表、更新array、delete链表、下一位数循环开始。然后善用调试(F5)
// 基类排序
#include"list.h"
int Getdigit(int x,int y); //得到x的第y位数字
void Radixsort(int arr[]);
void printarr(int arr[],int n);
#define N 10
#define B 10
#define S 3
int main()
{
int array[N]{
14,51,213,4,123,56,76,99,13,41};
cout<<"before sorting:\n";
printarr(array,N);
Radixsort(array);
cout<<"After sorting:\n";
printarr(array,N);
}
int Getdigit(int x,int y)
{
int i,t=1;
for(i=0;i<y;i++)
{
t*=10;
}
return (x%t)/(t/10);
}
void Radixsort(int arr[])
{
int i,j,k,digit;
List bucket[B];
for(i=0;i<S;i++)
{
for(j=0;j<B;j++)
{
bucket[j]=MakeEmpty(bucket[j]);
}
for(j=0;j<N;j++)
{
digit = Getdigit(arr[j],i+1);
InsertBack(arr[j],bucket[digit]);
//cout<<"tag1 and digit and arr"<<j<<" "<<digit<<" "<<arr[j]<<endl;
}
// for(j=0;j<B;j++)
// cout<<"bucket"<<j<<" "<<bucket[j]->num<<endl;
k=0;
for(j=0;j<B;j++)
{
Position temp = bucket[j];
while(temp->next!=nullptr)
{
temp = temp->next;
arr[k++]=temp->num;
// cout<<"tag2 and arr"<<k-1<<" "<<arr[k-1]<<endl;
}
}
for(j=0;j<B;j++)
{
DeleteList(bucket[j]);
//cout<<"tag3\n";
}
}
}
void printarr(int arr[],int n)
{
for(int i =0;i<n;i++)
cout<<arr[i]<<" ";
}