链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。链表有很多种不同的类型:单向链表,双向链表以及循环链表 等等。
链表的优点: 使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。
链表的缺点:链表允许插入和移除表上任意位置上的节点,但是不允许随机存取,由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(1)。
其实在使用链表的时候我们关心的最多的都是逻辑位置并不在乎其的物理位置
如下图更能表示链表的实际情况:
实现代码:
/*************************************
*copyright (c)2015,WK Studios
* Filename: S_List.h
*Compiler: GCC,VS,VC6.0 win32
*Author:WK
*Time: 2015 26 4
****************************************/
#ifndef _S_LIST_H
#define _S_LIST_H
#include<iostream>
//#include<malloc.h>
#include<assert.h>
using namespace std;
#define ElemType int
typedef struct node
{
ElemType data;
struct node *next;
}node,*Pnode;
typedef struct Mange
{
Pnode first;
Pnode last;
size_t size;
}Mange;
//1.初始化链表
void InitList(Mange *L);
//2.头插法
bool PushFront(Mange *L,ElemType e);
//4.尾插法
bool PushBack(Mange *L,ElemType e);
//3.显示
void Print(Mange *L);
//5.头删
bool PopFront(Mange *L);
//6.尾删
bool PopBack(Mange *L);
//7.按值查找,返回该节点
node* find_val(Mange *L,ElemType e);
//8.按值删除
bool del_val(Mange *L,ElemType e);
//9.逆转链表
void Reverse_Posi_List(Mange *L);
//10.求出前驱
bool Pre_Node(Mange *L,ElemType e1,ElemType &e);
//11.求出后继
bool Next_Node(Mange *L,ElemType e1,ElemType &e);
//12.清空链表
bool Clear_List(Mange *L);
//13.销毁链表
bool Destoy_List(Mange *L);
//14.根据改节点的值计算出该节点是第几个节点(从1开始计数)
size_t Calcu_Node(Mange *L,ElemType e);
//15.根据节点的值在某个节点的前面插入节点
bool Pre_insert_Val(Mange *L,ElemType e1,ElemType e);
//16.根据节点的值在某个节点的后面插入节点
bool Tail_insert_Val(Mange *L,ElemType e1 ,ElemType e);
//17.改变链表值进行排序
bool Sort_Val_List(Mange *L);
//18.改变链表位置进行排序
bool Sort_Pos_List(Mange *L);
//19.按顺序插入
bool insert_val(Mange *L, ElemType x);
#endif
/**********************************************************************
* Copyright (c)2015,WK Studios
* Filename: S_List.cpp
* Compiler: GCC,VS,VC6.0 win32
* Author:WK
* Time: 2015 26 4
**********************************************************************/
#include"S_List.h"
//1.建立头节点并初始化
void InitList (Mange *L)
{
node *s=(node*)malloc(sizeof(node));
assert(s != NULL);
s->data=-1000000;//此处也可以放其他信息,这里表示没有放信息
s->next=NULL;
L->first=L->last=s;
L->size=0;
}
//2.头插法
bool PushFront(Mange *L,ElemType e)
{
node *s=(node*)malloc(sizeof(node));
assert(s != NULL);
s->data=e;
s->next=L->first->next;
L->first->next=s;
if(L->size == 0)
{
L->last=s;
}
L->size++;
return true;
}
//4.尾插法
bool PushBack(Mange *L,ElemType e)
{
node *s=(node*)malloc(sizeof(node));
assert(s != NULL);
s->data=e;
s->next=NULL;
L->last->next=s;
L->last=s;
L->size++;
return true;
}
//3.显示
void Print(Mange *L)
{
if(L->first->next==NULL)
{
cout<<"空链表!\n";
return;
}
node *p=L->first->next;
while(p!= NULL)
{
cout<<p->data;
if(p->next!=NULL)
{
cout<<"->";
}
p=p->next;
}
cout<<"\n";
}
//5.头删
bool PopFront(Mange *L)
{
if(L->size==0)
{
return false;
}
node *p=L->first->next;
if(L->size==1)
{
free(p);
p=NULL;
L->first->next=NULL;
L->first=L->last;
}
else
{
L->first->next=p->next;
free(p);
p=NULL;
}
L->size--;
return true;
}
//6.尾删
bool PopBack(Mange *L)
{
if(L->size==0)
return false;
node *p=L->first;
while(p->next!=L->last)
{
p=p->next;
}
p->next=NULL;
free(L->last);
L->last=NULL;
L->last=p;
L->size--;
return false;
}
//7.按值查找,返回该节点
node* find_val(Mange *L,ElemType e)
{
node *p=L->first->next;
while(p!=NULL && p->data!=e)
{
p=p->next;
}
return p;
}
//8.按值删除
bool del_val(Mange *L,ElemType e)
{
if(find_val(L, e)==NULL)
{
cout<<"This Value Not Exist!\n";
return false;
}
else if(L->size==1)
{
PopBack(L);
return true;
}
else
{
node *p=find_val(L,e);
p->data=p->next->data;//并未真正删除节点只是将这个节点的下一个节点的值赋给自己并将这个节点踢出链表
p->next=p->next->next;
return true;
}
/*以下是else{}中可以真正删除节点的做法
node *p=L->first;
while(p->next!=NULL && p->next->data!=e)
{
p=p->next;
}
node *q=p->next;
p->next=q->next;
free(q);
q=NULL;
*/
}
//9.逆转链表(位置改变)
void Reverse_Posi_List(Mange *L)
{
node *p=NULL;//指向将要逆转的节点的前一个节点
node *r=L->first->next;//指向将要逆转的节点
node *q=NULL;//指向将要逆转的节点的下一个节点
while(r != NULL)
{
q=r->next;
r->next=p;//逆转操作,前一个节点的地址赋给本地节点的指针域
p=r;
r=q; //r指针向后移动
}
L->first->next=p;
}
//10.求出前驱
bool Pre_Node(Mange *L,ElemType e1,ElemType &e)
{
node *q=NULL;
node *p=L->first->next;
if(p==NULL)
{
cout<<"只有头节点!\n";
return false;
}
while(p->next)//找位置
{
q=p->next;
if(q->data==e1)
{
e=p->data;
return true;
}
p=q;//p向后移动
}
if(p->data==e1 && L->size==1)
{
cout<<"该值存在但是无前驱!\n";
}
else
{
cout<<"该值不存在!\n";
}
return false;
}
//11.求出后继
bool Next_Node(Mange *L,ElemType e1,ElemType &e)
{
node *p=L->first->next;
if(p==NULL)
{
cout<<"只有头节点!\n";
return false;
}
while(p->next)
{
if(p->data==e1)
{
e=p->next->data;
return true;
}
p=p->next;//p向后移动
}
if(p->data==e1)
{
cout<<"该值存在但是无后继!\n";
}
else
{
cout<<"该值不存在!\n";
}
return false;
}
//12.清空链表(除过头节点全部释放空间)
bool Clear_List(Mange *L)
{
if(L->first->next==NULL)
{
cout<<"空链表!\n";
return false;
}
node *p=NULL;
p=L->first->next;
while(p)
{
node *q=p->next;
free(p);
p=NULL;
p=q;
}
L->last=L->first;
L->size=0;
L->first->next=NULL;
return true;
}
//13.销毁链表(头节点空间也释放)
bool Destoy_List(Mange *L)
{
node *q=L->first;
while(q)
{
node *p=q->next;
free(q);
q=p;
}
L->size=0;
return true;
}
//14.根据该节点的值计算出该节点是第几个节点(从1开始计数)
size_t Calcu_Node(Mange *L,ElemType e)
{
if(L->first->next==NULL)
{
cout<<"空链表!\n";
return 0;
}
size_t i=1;
node *p=L->first->next;
while(p!=NULL && p->data!=e)
{
++i;
p=p->next;
}
if(p==NULL)
{
cout<<"该值不存在!\n";
return 0;
}
return i;
}
//15.根据节点的值在某个节点的前面插入节点
bool Pre_insert_Val(Mange *L,ElemType e1 ,ElemType e )
{
node *s=(node*)malloc(sizeof(node));
assert(s != NULL);
s->data=e1;
s->next=NULL;
node *q=L->first;
node *p=L->first->next;
while(p!=NULL && p->data!=e)
{
q=q->next;
p=p->next;
}
if(L->size==0)//判断当么有节点时候
{
PushFront(L, e1);
return true;
}
s->next=q->next;
q->next=s;
return true;
}
//16.根据节点的值在某个节点的后面插入节点
bool Tail_insert_Val(Mange *L,ElemType e1 ,ElemType e)
{
node *s=(node*)malloc(sizeof(node));
assert(s != NULL);
s->data=e1;
s->next=NULL;
if(L->size==0)//考虑空链表的时候
{
PushBack(L, e1);
return true;
}
node *p=L->first->next;
while(p->next)//找位置
{
if(p->data==e)
{
break;
}
p=p->next;//p向后移动
}
if(p->data==e)//考虑在最后一个数后面插入
{
s->next=p->next;
p->next=s;
L->last=s;
return true;
}
s->next=p->next;
p->next=s;
return true;
}
//17.改变链表值进行排序(选择排序,从小到大)交换值
bool Sort_Val_List(Mange *L)
{
node *p,*q,*small;
ElemType temp;
if(L->size<2)
{
cout<<"只有1个或者0个节点,无需排序!\n";
return true;
}
for(p=L->first->next;p->next!=NULL;p=p->next)
{
small=p;
for(q=p->next;q;q=q->next)
{
if(q->data<small->data)
{
small=q;
}
}
if(small!=p)
{
temp=p->data;
p->data=small->data;
small->data=temp;
}
}
return true;
}
//18.改变链表位置进行排序(选择排序,从小到大)交换地址
bool Sort_Pos_List(Mange *L)
{
if(L->size<2)
{
cout<<"只有1个或者0个节点,无需排序!\n";
return true;
}
node *p=L->first->next;
node *q=p->next;
L->last=p;
p->next=NULL;
while(q!=NULL)
{
p=q;
q=q->next;
insert_val(L,p->data);
}
return true;
}
//19.按顺序插入
bool insert_val(Mange *L, ElemType x)
{
node *p = find_val(L,x);
node *s = (node *)malloc(sizeof(node));
assert(s != NULL);
s->data = x;
p = L->first;
while(p->next != NULL)
{
if(x < p->next->data)
break;
p = p->next;
}
//插入p的后面
s->next=p->next;
p->next=s;
if(p == L->last)
{
L->last=s;
}
return true;
}
/*************************************
*copyright (c)2015,WK Studios
* Filename: main.cpp
*Compiler: GCC,VS,VC6.0 win32
*Author:WK
*Time: 2015 25 4
****************************************/
#include"S_LIST.h"
int main()
{
Mange mylist;
InitList(&mylist);
int select=1;
ElemType item,item1;
node *p=NULL;
while(select)
{
cout<<"****************************************************\n";
cout<<"* [1]PushFront [2]PushBack *\n";
cout<<"* [3]Print [4]PopFront *\n";
cout<<"* [5]PopBack [6]del_val *\n";
cout<<"* [7]Reverse_Posi_List [8] Pre_Node *\n";
cout<<"* [9]Next_Node [10] Calcu_Node *\n";
cout<<"* [11]Pre_insert_Val [12] Tail_insert_Val *\n";
cout<<"* [13]Sort_Val_List [14]Sort_Pos_List *\n";
cout<<"* [15]insert_val [16] *\n";
cout<<"* [17] [18]Clear_List *\n";
cout<<"* [19]Destoy_List [0] Quit *\n";
cout<<"****************************************************\n";
cout<<"Please input a number which is represent a operator you want to do:";
cin>>select;
switch(select)
{
case 1:
cout<<"please input number you want to insert:(end of number is -1)";
while(cin>>item,item!=-1)
{
PushFront(&mylist,item);
}
break;
case 2:
cout<<"please input number you want to insert:(end of number is -1)";
while(cin>>item,item!=-1)
{
PushBack(&mylist,item);
}
break;
case 3:
Print(&mylist);
break;
case 4:
PopFront(&mylist);
break;
case 5:
PopBack(&mylist);
break;
case 6:
cout<<"please input number you want to delete:(end of number is -1)";
while(cin>>item,item!=-1)
{
del_val(&mylist,item);
}
break;
case 7:
Reverse_Posi_List(&mylist);
break;
case 8:
cout<<"please input number you want to get that's precursor:(end of number is -1)";
while(cin>>item,item!=-1)
{
if(Pre_Node(&mylist,item, item1))
{
cout<<"precursor of "<< item<<" is "<<item1<<"\n";
}
}
break;
case 9:
cout<<"please input number you want to get that's succeed:(end of number is -1)";
while(cin>>item,item!=-1)
{
if(Next_Node(&mylist,item, item1))
{
cout<<"succeed of "<<item<<" is "<< item1<<"\n";
}
}
break;
case 10:
cout<<"please input number you want to get that's succeed:(end of number is -1)";
while(cin>>item,item!=-1)
{
cout<<"是第"<<Calcu_Node(&mylist,item)<<"个节点!\n";
}
break;
case 11:
while(cout<<"请输入想要插入的数(-1结束):",cin>>item1,
cout<<"请输入想要在哪个数前面插入刚才输入的数(如果是给空链表插入的话就输入0)(-1结束):",
cin>>item,item!=-1)
{
Pre_insert_Val(&mylist,item1,item);
}
break;
case 12:
while(cout<<"请输入想要插入的数(-1结束):",cin>>item1,
cout<<"请输入想要在哪个数前面插入刚才输入的数(如果是给空链表插入的话就输入0)(-1结束):",
cin>>item,item!=-1)
{
Tail_insert_Val(&mylist,item1,item);
}
break;
case 13:
Sort_Val_List(&mylist);
break;
case 14:
Sort_Pos_List(&mylist);
break;
case 15:
cout<<"please input number you want to insert:(end of number is -1)";
while(cin>>item,item!=-1)
{
insert_val(&mylist,item);
}
break;
case 16:
break;
case 17:
break;
case 18:
Clear_List(&mylist);
break;
case 19:
Destoy_List(&mylist);
cout<<"链表已经销毁\n";
default:
cout<<"Thank you for using !\n";
break;
}
}
return 0;
}