链表是一种重要的数据结构,能够动态地进行存储分配的一种结构链表,它与数组比较是不用事先确定存储空间,
链表的实现方式有两种(其实是我只会这两种)
第一种:是通过结构体指针实现。(课本上讲的就是这种方法)
第二种:是通过数组模拟实现。即链式向前星(不需要掌握)
这两种只是实现方式不同,本质是相同的;
下面讲解单链表如何实现:
首先链表的每个节点分为数据域和指针域;
头指针Head指向第一个元素
第一个元素存储第一个数据和指针域指向第二个元素的地址 (第二个元素不必要紧挨着第一个元素)
第二个元素存储第二个数据和指针域指向第三个元素的地址
第三个元素存储第三个数据并且指针域指向空NULL
假设有三个元素:
a[0]包括数据部分 通过next指针指向a[1]的地址
a[1] 包括数据部分 通过next指针指向a[2]的地址
a[2] 包括数据部分 通过next指针NULL (一共三个元素,最后一个元素指向NULL)
最后一个元素中 next 指针域指向NULL
1.在数据域中可以定义多种数据类型并存放数据,
例如,char ,double ,int…
2.
链表的定义:
struct node{
int data; //数据域,所存的值
node *next; //指针域,只想下一个元素
};
创建头节点并初始化表头:
node*head,*pre,*p;
head=new node; //创造头结点
head->next=NULL;
假设将arr[ ]={}插入到链表
//链表的创建//
node* creatlist(int arr[],int len){ //建立链表
node*head,*pre,*p; //头节点 尾结点 普通节点
head=new node; //创造头结点
head->next=NULL;
pre=head; //pre赋值 ,pre指向于head;
for(int i=0;i<len;i++){
p=new node;
p->data=arr[i]; //赋值给数据域
p->next=NULL; //最后一个节点的指针域置空
pre->next=p; //新结点连到链表的结尾 ,上一个节点指向这个新结点
pre=p; //pre指向链表的最后(新)一个结点
}
return head;
}
//链表的删除//
void del(node*head,int x){ //实现将链表所有数据域等于x的结点删除
node *pre,*p;
pre=head; //pre指针始终指向被删除结点的前置结点
p=pre->next; //p指针为工作指针,用于遍历链表
while(p!=NULL){
if(p->data ==x){ //如果是要被删除的结点
pre->next =p->next;
delete(p); //要记得用过的内存还给操作系统
p=pre->next ; //p指针更新到pre指针的后面
}
else {
pre=p; //如果不是要删除的结点那么两个指针分别后移,这一步该为pre=pre->next;
p=p->next ;
}
}
}
完整代码:
#include<iostream>
#include<stdlib.h>
using namespace std;
//链表的定义//
struct node{
int data; //数据域
node *next; //指针域,只想下一个元素
};
//链表的创建//
node* creatlist(int arr[],int len){ //建立链表
node*head,*pre,*p; //头节点 尾结点 普通节点
head=new node; //创造头结点
head->next=NULL;
pre=head; //pre 对pre操作就是对head操作;
for(int i=0;i<len;i++){
p=new node;
p->data=arr[i]; //赋值给数据域
p->next=NULL; //最后一个节点的指针域置空
pre->next=p; //新结点连到链表的结尾 ,上一个节点指向这个新结点
pre=p; //pre指向链表的最后(新)一个结点
}
return head;
}
//链表的插入//
void insert(node*head,int n,int x){ //实现将一个数据插入到链表的指定位置n处
node*p,*pre;
pre=head;
p=new node; //分配新结点
p->data=x;
while(--n){ //将pre指针定位到n位置的前面
pre=pre->next;
}
p->next=pre->next; //先将pre的next赋值给p ,将pre所指结点后面所有的结点连接到p所指的结点
pre->next=p; //将p所指的结点链接到pre所指结点后面。
}
//链表的删除//
void del(node*head,int x){ //实现将链表所有数据域等于x的结点删除
node *pre,*p;
pre=head; //pre指针始终指向被删除结点的前置结点
p=pre->next; //p指针为工作指针,用于遍历链表
while(p!=NULL){
if(p->data ==x){ //如果是要被删除的结点
pre->next =p->next;
delete(p); //要记得用过的内存还给操作系统
p=pre->next ; //p指针更新到pre指针的后面
}
else {
pre=p; //如果不是要删除的结点那么两个指针分别后移,这一步该为pre=pre->next;
p=p->next ;
}
}
}
//主函数//
int main()
{
int arr[7]={2,0,2,1,6,9}; //这个是例子。
node*L=creatlist(arr,7),*p;
insert(L,2,12);
del(L,2);
p=L->next;
while(p!=NULL){
printf("%d ",p->data);
p=p->next;
}
return 0;
}
链表
链表本身是离散化的,各点之间通过指针相连;
链表的每一结点都可存多种不同的数据类型每个结点都有一个指向
多次用到结构体,指针的操作,
1.顺序存储和链式存储
顺序表的存储空间是一次性分配的,且是连续的存储空间
链表的存储空间是多次分配的,不需要是连续的
2.插入删除时移动元素的个数
顺序表插入删除时需要移动近一半的元素(时间复杂度为O(n))
链表不需要移动元素,只需移动指针
null在值上等于零;
new开辟的空间在堆上,而一般声明的变量存放在栈上。通常来说,当在局部函数中new出一段新的空间,该段空间在局部函数调用结束后仍然能够使用
可用于存储图及对图进行一系列操作
例如昨天数据库老师讲的死锁的诊断—即判断链表是否存在环;
数组模拟链表
#include<iostream>
using namespace std;
int e[100010],head,ne[100010],idx;
//链表初始化;
void init(){
head=-1;
idx=0;
}
//向头部插入元素
void add_to_head(int x){
e[idx]=x;
ne[idx]=head;
head=idx++;
}
void add(int k,int x){
e[idx]=x;
ne[idx]=ne[k];
ne[k]=idx++;
}
void remove(int k){
// if(k==0)head=ne[head];
ne[k]=ne[ne[k]];
}
for(int i=head;i!=-1;i=ne[i])cout<<e[i]<<" ";
return 0;
}