单链表是线性结构的,访问单链表需要从头开始访问,即便是双链表也不可能做到二分查找。
可以实现二分查找的是顺序表,请这位网友确认一下自己的文章的正确性!
https://www.jianshu.com/p/874be30537d7
本文如有错误欢迎指正!
目录
单链表
带头节点的单链表
不带头节点的单链表
单链表:单向列表,只能按从头到尾的顺序进行访问
分类: 带头节点的单链表 不带头节点的单链表
一般操作:
一般情况下,我个人比较喜欢使用头节点统计链表长度。没什么需要特殊注意的地方。
【注】代码均在devc++编译器上跑过,可以运行,如果使用vc++这个编译器,需要注意 i 可能重复定义!
【注】因为删除、修改都需要用到查询这个操作,所以不再写查询函数。
还有,这都什么年代了,还在用 vc++ 6.0 ? 这是我用过最麻烦的一个编译器。
#include
#include
/**
这是一个带头节点的链表,Head作为头节点,Head->value 用于统计链表长度
这是最常见也是最方便的方法,如果不是用头节点统计量表长度,就需要遍历链表来统计链表长度
*/
typedef struct LNode{
int value;
struct LNode *next;
}Lnode,*Linklist;
// *Lnode 等价于 Linklist
Linklist initList(){
//初始化链表,给链表开辟一个头节点的空间
Lnode *Head = (Linklist)malloc(sizeof(Lnode));
Head->next = NULL;
Head->value = 0;
return Head;
}
//顺序插入 一组数据
/**
顺序插入可以理解为 给p节点赋值,将 q节点连接到 p节点上,q 节点只是为了给p节点开辟新空间
*/
Linklist Insert_into_List_at_before(Linklist &Head){
//Inserting elements into List at the before
//开辟一个p节点,并将Head与之相连
Lnode *p = (Linklist)malloc(sizeof(Lnode)),*q;
p->next = NULL;
Head->next = p;
int x;
while(x != 10){
// End with 10 as input flag, and 10 is the last element of inputing;
scanf("%d",&x);
//开辟 一个 q节点空间
q = (Linklist)malloc(sizeof(Lnode));
//q 空间指向空表示链表结束
q->next = NULL;
// 将数据存入 p 节点
p->value = x;
//将 q 节点与 p 节点连接
p->next = q;
// p 节点后移到了原来 q节点的位置
p = p->next;
// 链表长度 +1
Head->value ++;
}
return Head;
}
//逆序插入 一组数据
/**
逆序插入可以理解为给q 节点赋值,p节点只是起一个连接作用
*/
Linklist Insert_into_List_at_fore(Linklist &Head){
//Inserting elements into List at fore
//开辟 p 空间
Lnode *p = (Linklist)malloc(sizeof(Lnode)),*q;
p->next = NULL;
Head->next = p;
int x;
while(x != 10){
scanf("%d",&x);
//开辟 q 空间
q = (Linklist)malloc(sizeof(Lnode));
//给 q 节点赋值
q->value = x;
//将 q 连接到 p 上
q->next= p->next;
//p 后移,到了原来 q 上
p->next = q;
printf("%d ",p->next->value);
//链表长度+1
Head->value ++;
}
//这一步是将 p节点删除,删除的原因是 p节点上没有存储数据,p->value是一个不确定的数,不是我们要存储的数据
Head ->next = p->next;
return Head;
}
//前插法输出
void display(Linklist Head){
// Traversing the list in order
Linklist p;
p = Head->next;
int i = 0;
// 不断后移直到链表尾
while(p->next!=NULL){
printf("Locate at %d value = %d\n",i,p->value);
p = p->next;
i ++;
}
}
//尾插法插入一个数据
/**
待插节点为 r
尾插法需要找到插入的位置 locate,p 指向 locate的前一个节点,q 指向 locate节点
r->next = q;
p->next = r;
这样子就可以了。
但是考虑到用户输入的位置可能超过链表长度,所以如果超过链表长度,就直接将 r 节点插入到最后
[注]: 必须先 r->next= q 后 p->next= r,这是因为顺序反了会造成 q节点后面的部分丢失。
前插法一样的道理。
*/
Linklist Insert_into_inner_list_as_before(Linklist &Head){
int x,locate;
printf("input locate and number\n");
scanf("%d%d",&locate,&x);
Lnode *p,*q,*r;
p = Head;
r = (Linklist)malloc(sizeof(Lnode));
r->value = x;
int i = 0;
//判断 locate是否超过链表长度 [注]链表长度从 0 开始计数
if(locate < (Head->value-1)){ //长度未超过直接插入
while(i < locate-1){
p= p->next;
}
q = p->next;
r->next = q;
p->next = r;
}
else { //超过链表长度插入到链表尾部
while(p->next !=NULL){
p= p->next;
}
r->next = NULL;
p->next = r;
}
Head->value ++;
return Head;
}
//前插法插入一个数据
/**
待插节点为 r
前插法需要找到待插位置 locate ,p 指向locate 前一个节点, q 指向locate节点
r->next = q;
p->next = r;
这样子就可以了 。
考虑用户输入的locate 可能超过链表长度,这时候直接插到尾部
*/
Linklist Insert_into_inner_list_as_fore(Linklist &Head){
int x,locate;
printf("input locate and number\n");
scanf("%d%d",&locate,&x);
Lnode *p,*q,*r;
p = Head;
r = (Linklist)malloc(sizeof(Lnode));
r->value = x;
int i = 0;
while(i < locate){
p= p->next;
//printf("%d ",p->value);
i ++;
}
r->next = p->next;
p->next = r;
Head->value ++;
return Head;
}
//按位置删除 一个数据
/**
按位置和按值删除都是先要找到 locate ,p指向 locate 前一个节点,p指向locate
删除节点就是让 p->next = q->next ,这样就直接越过了locate节点。
*/
Linklist Delete_by_locate(Linklist &Head){
printf("Head->value =%d\n",Head->value);
int locate;
printf("input locate \n");
scanf("%d",&locate);
Lnode *p,*q,*r;
p = Head;
int i = 0 ;
if(locate <= 0){
Head->next = Head->next->next;
printf("delete %d",Head->next->value);
Head->value --;
return Head;
}
if(locate >= Head->value){
while(p->next->next != NULL){
p= p->next;
}
printf("delete %d",p->next->value);
p->next = NULL;
Head->value --;
return Head;
}
while(inext;
i++;
}
printf("delete %d",p->next->value);
p->next = p->next->next;
Head->value --;
return Head;
}
//尾插法输出
void display_fore(Linklist Head){
printf("\n-------------------------Here display list in order-------------------------\n");
int i = 0;
Linklist p;
p = Head->next;
printf("Head->value = %d\n",Head->value);
while(p!=NULL){
printf("Local %d value = %d\n",i,p->value);
p = p->next;
i ++;
}
printf("\n-------------------------Display completed -------------------------\n\n");
}
//计算链表长度
/**
如果头节点不计数,就需要这个函数遍历整个链表求链表长度
*/
int get_Element_num(Linklist Head){
// if head node didn't count the amount, this method Traversing the list in order and count the amount
Linklist p;
p = Head->next;
int i = 0;
while(p->next!=NULL){
p = p->next;
i ++;
}
printf("length of list = %d\n",i);
return i;
}
//按数值删除数据
Linklist Delete_by_number(Linklist &Head){
printf("\n-------------------------Here drop node by numbers-------------------------\n");
int number;
printf("input number : ");
scanf("%d",&number);
Lnode *p,*q;
q = Head;
p = Head->next;
int i=0,count = Head->value ;
//q 节点永远在 p节点前面 所以使用 q节点
while(q->next != NULL){
//将所有等于 x 的节点都删除
//p 循环是为了删除连续等于 x 的节点
while(p->value == number ){
//如果 p 是最后一个节点仍然等与 x,那么q作为链表最后一个节点;
if(p->next == NULL){
q->next = NULL;
break;
}
else{
printf("value = %d\n",p->value);
q->next = p->next;
printf("q->next->value = %d\n",q->next->value);
p = p ->next;
}
Head->value --;
}
//如果不加这个判断程序会崩溃,别问我为什么,我花了一个半小时都没搞懂
//大概是因为q->next已经指向空,那么循环就会继续下去,不会因为q->next == NULL 退出循环
if(q->next == NULL) {
printf("I arrived at NULL\n");
break;
}
else{
q = q->next;
p = p->next;
}
}
printf("Delete sucessfully \n");
printf("\n-------------------------Delete sucessfully -------------------------\n");
return Head;
}
Linklist update_by_num(Linklist &Head){
printf("\n-------------------------Here update x by y-------------------------\n");
Linklist p,q;
int x;int y;
printf("input y to replace x: \n");
scanf("%d",&x);
scanf("%d",&y);
q = Head->next;
while(q->next != NULL){
if(q->value == x){
q->value = y;
}
q = q->next;
}
if(q->value == x) q->value = y;
printf("Update sucessfully \n");
printf("\n-------------------------Update sucessfully -------------------------\n");
return Head;
}
Linklist update_by_locate(Linklist &Head){
printf("\n-------------------------Here replace node locate at x whith number y-------------------------\n");
int x,y;
scanf("%d",&x);
scanf("%d",&y);
int i = 0 ;
Linklist q = Head->next;
if(x > Head->value-1)
printf("Overflowed ! \nCheck y so that guarantee not to exceed the list length!");
while(q->next != NULL){
printf("%d ",q->value);
if(i == x){
q->value = y;
break;
}
i ++;
q = q->next;
}
if(i == x){
q->value = y;
}
printf("\n-------------------------Update sucessfully -------------------------\n");
return Head;
}
int main(){
Linklist head = initList();
}
不带头节点可能比带头单链表稍微麻烦一点,问题也不大。
[注]不带头除了从第一个节点开始存储数据外,其他和带头节点差不多。
[注]构造函数不同,无头单链表构造时,不需要开辟空间,L= NULL即可,而带头单链表构造时,需要给L开辟空间,
并且使L->next = NULL!
#include
#include
typedef struct LNode{
int value;
struct LNode *next;
}Lnode,*Linklist;
// 构造第一个节点,指向空
Linklist initList(Linklist &L){
L = NULL;
return L;
}
//无头单链表和带头 单链表的最大区别就是无头单链表从第一个节点开始存储数据 ,
//而带头单链表是 从第二个节点开始存储数据 ,其他都一样
Linklist insert(Linklist &L){
L = (Linklist)malloc(sizeof(Lnode));
L->next = NULL;
int x;
printf("input\n");
//第一个节点存储数据
scanf("%d",&x);
L->value = x;
Linklist p = L;
while(x!=10){
printf("input\n");
scanf("%d",&x);
Linklist q =(Linklist)malloc(sizeof(Lnode));
q->value = x;
q->next = NULL;
p->next = q;
p = p->next;
}
return L;
}
//注意从第一个节点开始存储 数据
//尾插法
Linklist insert_single(Linklist &L){
int x;
scanf("%d",&x);
//插入第一个数据时,L节点没有分配内存,需要分配内存,然后直接够L赋值,next指向空
if(L == NULL){
L = (Linklist)malloc(sizeof(Lnode));
L->value = x;
L->next = NULL;
return L;
}
//往后插入和带头链表的操作一样
else{
Linklist p = L,q;
while(p->next != NULL){
printf("move right p->value = %d\n",p->value);
p = p->next;
}
q = (Linklist)malloc(sizeof(Lnode));
q->value = x;
q->next = NULL;
p->next= q;
}
return L;
}
//前插法
Linklist insert_single_fore(Linklist &L){
int x;
scanf("%d",&x);
//插入第一个数据时,L节点没有分配内存,需要分配内存,然后直接够L赋值,next指向空
if(L == NULL){
L = (Linklist)malloc(sizeof(Lnode));
L->value = x;
L->next = NULL;
return L;
}
//往后插入只需要开辟一个p空间,存放数据,next指向 L,在让L等于p就可以了
else{
Linklist p = (Linklist)malloc(sizeof(Lnode)),q = L;
p->next= q;
p->value = x;
L = p;
return L;
}
}
void display(Linklist L){
printf("arrived\n");
Linklist p = L;
while(p->next != NULL){
printf("%d ",p->value);
p = p->next;
}
printf("%d ",p->value);
}
int main(){
Linklist L;
initList(L);
//insert(L);
//display(L);
while(1){
insert_single_fore(L);
display(L);
}
}