单链表

来源:互联网 发布:怎么开淘宝 编辑:程序博客网 时间:2024/06/18 12:07

既然说链表是重中之重,好吧,学习单链表

基本的操作有创建、查找、插入、删除、等等

面试时通常是:创建一个链表(单链表/双向链表),并写出基于该链表的查找、插入、删除算法代码。我的个神

先说说线性表:

说到链表,不得不提线性表,通常线性表是顺序存储结构。线性表的特点是两个数据元素在逻辑上是相邻的,在物理存储上也是相邻的。

这就是线性表

通常说的先进先出的队列,先进后出的栈都是线性表的数据结构。

线性表算法简单实现起来也方便快捷,但是,它的存储特点决定了它的局限性。

尤其是:(1)在作插入删除操作时,要移动大量内存数据。

(2)线性表的总长度一旦确定就不能改变了,除非重新分配内存。比如用realloc

为了克服这些不足,就出现了非顺序存储结构的链表。

重要的是:链表可以方便的实现 前面提到的 队列 的先进先出 栈的 后进先出操作

线性结构和非线性结构很好理解。

线性结构如线性表、链表

网上通俗的解释是:

线性表 就是ABC这样一个个数据按顺序下去的。他们的内存储存也是ABC这样一个个下去的,|A|B|C|...|。就是内存中要连续储存线性链表 就是数据看上去是ABC这样下去的,内存里面是|B|...|C|...|A|...|这样的混乱顺序排列的,就是内存中储存位置任意。他们用节点指针连接。也就是说A数据后面还有个指针指向B数据的内存地址。链表 顾名思义包括线性链表,还有什么循环链表,双向链表啊啥的。。给你画个图把..链表的。(线性链表)(A,B,C,D,E)储存地址  数据  指向地址1         B        99   18        E        NULL28        D        1850        A        199        C        28这样的话他们储存在1.18。28.50.99.位置上,而且顺序乱来,但显示出来却是(A,B,C,D,E)

非线性结构如树、图等


好了,言归正传!

所谓单链表是指:数据节点是单向排列的,一个单链表节点,其结构分为两部分,1)数据域:用于存储数据元素的值。2)指针域(链域):用于存储下一个结点的地址,或者说指向其直接后继结点的指针。


一般声明3个指针来操作链表(不限于三个)可以更改,视具体情况而定

Head:用来指向指针的头部,头指针。

p1:用来指向新结点,用来遍历链表的每一个结点

p2:用来指向当前结点。

程序示例:

//创建一个n个元素的单链表#include <stdio.h>#include <malloc.h>//其实也可以写#include <stdlib.h>,因为#include <stdlib.h>中包含了#include <malloc.h>#include <conio.h>typedef struct node{int num;struct node *next;}student;student *create(int n){student *head,*p1,*p2;//已经解释过,head表示指针头,p1表示指向下一个结点,p2表示指向当前结点head = p1 = p2 = NULL;for (int i = 0;i < n; i++){p1 = (student*)malloc(sizeof(student));p1->num = i;if (i == 0){head = p1;}else{p2->next = p1;}p2 = p1;}p2->next = NULL;return p2;}int main(){student *link;for (int i = 1; i <= 5; i++){link = create(i);printf("%d ",link->num);}printf("\n");getch();return 0;}
结果如下:


断网了,下次继续~~~


单链表的查找算法

链表的查找算法思路是:

因为要对链表进行查找,而单链表比较蠢,所以只能从最开始的头指针开始

这就需要两个条件

1)单链表的头指针

2)要查询的数值

比如定义的单链表结构体如下:

typedef struct node{int num;struct node *next;}student;
那么查找单链表子函数可以写成

//链表按值查找  void find(student *head, int date)  {  student *p;  int n=1;  p = head;  while(NULL != p && date != p->num)  {  p = p->next;  n++;  }  if(NULL == p)  {//printf("链表中只有一个值%d\n",head->num);printf("链表中没有找到该值");  }else if(date == p->num)  {  printf("要查找的值%d在链表中第%d个位置\r\n", date, n);  }  return;  } 
这是按值查找,按值查找就是以要查询的值为中心,从头指针开始,顺序搜索。直到找到正确的值。

具体实现如下:

#include <stdio.h>#include <malloc.h>//其实也可以写#include <stdlib.h>,因为#include <stdlib.h>中包含了#include <malloc.h>#include <conio.h>typedef struct node{int num;struct node *next;}student;student *create(int n){student *head,*p1,*p2;//已经解释过,head表示指针头,p1表示指向下一个结点,p2表示指向当前结点head = p1 = p2 = NULL;for (int i = 0;i < n; i++){p1 = (student*)malloc(sizeof(student));p1->num = i;if (i == 0){head = p1;}else{p2->next = p1;}p2 = p1;}p2->next = NULL;return head;}//链表按值查找  void find(student *head, int date)  {  student *p;  int n=1;  p = head;  while(NULL != p && date != p->num)  {  p = p->next;  n++;  }  if(NULL == p)  {//printf("链表中只有一个值%d\n",head->num);printf("链表中没有找到该值");  }else if(date == p->num)  {  printf("要查找的值%d在链表中第%d个位置\r\n", date, n);  }  return;  } int main(){student *f;f = create(10);find(f,1);getch();return 0;}
结果如下:



也可以按序号查找,就是从第一个序号(或者称位置遍历),直到找到正确的值

#include <stdio.h>#include <malloc.h>//其实也可以写#include <stdlib.h>,因为#include <stdlib.h>中包含了#include <malloc.h>#include <conio.h>typedef struct node{int num;struct node *next;}student;student *create(int n){student *head,*p1,*p2;//已经解释过,head表示指针头,p1表示指向下一个结点,p2表示指向当前结点head = p1 = p2 = NULL;for (int i = 0;i < n; i++){p1 = (student*)malloc(sizeof(student));p1->num = i;if (i == 0){head = p1;}else{p2->next = p1;}p2 = p1;}p2->next = NULL;return head;}//按序号查找  void research_Number(student *head, int Num)  {  student *p=head;  int i = 1;  while(NULL != p && i < Num)  {  p = p->next;  i++;  }  if(p == NULL)  {  printf("查找位置不合法\r\n");  }else if(i == 1)  {  printf("查找位置为头结点且为%d\r\n",p->num);  }else if(i == Num)  {  printf("第%d个位置数据为%d\r\n", i, p->num);  }  }  int main(){student *f;f = create(10);research_Number(f,1);research_Number(f,2);research_Number(f,3);getch();return 0;}

到此,查找告一段落了


删除

删除似乎麻烦一点

删除单链表算法的思路:

先声明两个指针,p1,p2

p1用来遍历链表,查找符合条件的结点。

p2用来指向当前结点。

具体如下:

#include <stdio.h>#include <malloc.h>//其实也可以写#include <stdlib.h>,因为#include <stdlib.h>中包含了#include <malloc.h>#include <conio.h>#include <iostream>typedef struct node{int num;struct node *next;}student;student *create(int n){student *head,*p1,*p2;//已经解释过,head表示指针头,p1表示指向下一个结点,p2表示指向当前结点head = p1 = p2 = NULL;for (int i = 0;i < n; i++){p1 = (student*)malloc(sizeof(student));p1->num = i;if (i == 0){head = p1;}else{p2->next = p1;}p2 = p1;}p2->next = NULL;return head;}student* del(student *head,int i){bool flag = false;if (head){student *p1,*p2;p1 = head;//关键的一步while (p1->num != i && p1->next != NULL ){p2 = p1;p1 = p1->next;}if (p1->num == i){if (p1 == head){head = p1->next;}else{p2->next = p1->next;}}free(p1);flag = true;}if (!flag){printf("\n %d could not been found",i);}return head;}//单链表测长  int length(student *head)  {  int n = 0;  student *p;  p = head;  while(p != NULL)  {  p = p->next;  n++;  }  return n;  } //单链表打印  void print(student *head)  {  node *p;  int n;  n = length(head);  std::cout<<"Now, These "<<n<<" records are: "<<std::endl;  p = head;  if(p != NULL)  {  while(p != NULL)  {  std::cout<<p->num<<" -> ";  p = p->next;  }  std::cout<<std::endl;  }  }  int main(){student *f;f = create(10);f = del(f,3);print(f);getch();return 0;}

结果为:




最后一个,插入:
插入的思路:同样定义两个指针p1,p2
p1用来遍历链表,查找符合条件的结点,p2用来指向当前结点
首先判断是不是空链表
如果不是空链表,判断要插入的位置
具体如下:

//插入单链表节点node *insert(node *head, int num){node *p1, *p2, *p3;p2 = head;p1 = (node *)malloc(sizeof(node));//先将要插入的节点申请下~p1->data = num;//思路同删除节点操作while(num > p2->data && p2->next != NULL) {p3 = p2;p2 = p2->next;}if(num <= p2->data){if(head == p2){p1->next = p2;head = p1;}else{p3->next = p1;p1->next = p2;}}else {//此处要细心,别忘了可以在尾部插入节点。p2->next = p1;p1->next = NULL;}return head;}

全部插入实现如下:

#include <iostream>#include <stdio.h>#include <string.h>#include <conio.h>typedef struct student{int data;struct student *next;}node;//建立单链表node* create(){node *head,*p,*s;int x, cycle = 1;head = (node *)malloc(sizeof(node));p = head;while(cycle){std::cout<<"please input the data: ";std::cin>>x;std::cout<<std::endl;if(x != 0){s = (node *)malloc(sizeof(node));s->data = x;p->next = s;p = s;}elsecycle = 0;}head = head->next;p->next = NULL;return head;}//单链表测长int length(node *head){int n = 0;node *p;p = head;while(p != NULL){p = p->next;n++;}return n;}//单链表打印void print(node *head){node *p;int n;n = length(head);std::cout<<"Now, These "<<n<<" records are: "<<std::endl;p = head;if(p != NULL){while(p != NULL){std::cout<<p->data<<" -> ";p = p->next;}std::cout<<std::endl;}}//插入结点node* insert(node *head, int num){node *p0,*p1,*p2;p1 = head;p0 = (node *)malloc(sizeof(node));//待插入的结点p0->data= num;while(p0->data > p1->data && p1->next != NULL){p2 = p1;p1 = p1->next;}if(p0->data <= p1->data){if(p1 == head){p0->next = p1;head = p0;}else{p0->next = p1;p2->next = p0;}}else{p1->next = p0;p0->next = NULL;}return head;}int main(int argc, char* argv[]){node *head;head = create();print(head);//删除结点int num;//插入结点std::cin>>num;head = insert(head, num);print(head);getch();return 0;}
结果:

到这里 插入也结束了

单链表是链表的基础,今后还要花时间多学习,先这样吧,更多细节请看这里,这里,这里

当然 中间排序什么的也很重要

如:

#include<iostream>using namespace std;typedef struct student{int data;struct student *next;}node;//创建单链表node *create(){node *head, *p, *s;int x;head = (node *)malloc(sizeof(node));//先创建一个头节点,便于头指针的操作。p = head;printf("Input the data, end with 0 : ");while(1){if(scanf("%d", &x) != EOF && x != 0){//如果输入数据合法,则再创建一个节点;否则跳出循环。s = (node *)malloc(sizeof(node));s->data = x;p->next = s;p = s;}else break;}head = head->next;p->next = NULL;return head;}//计算单链表长度int length(node *head){int n = 0;while(head != NULL){head = head->next;n++;}return n;}//打印单链表void print(node *head){int n = length(head);printf("Output the list %d records : ", n);while(head != NULL){printf(head->next == NULL ? "%d\n" : "%d->", head->data);head = head->next;}}//删除单链表节点node *del(node *head, int num){node *p1, *p2;if(head == NULL) return NULL;p1 = head;while(num != p1->data && p1->next != NULL){//若当前指针所存储的值与要删除的值不符,则向后遍历,直至相等或至最后一个节点。p2 = p1;p1 = p1->next;}if(num == p1->data){//如果链表中存在要删除的节点,则分两种情况:在头节点处或链表中。if(head == p1) head = p1->next;else p2->next = p1->next;free(p1);//别忘了释放删除的节点哦~节约点~}else printf("There is no '%d' \n", num);//找不到要删除的值。return head;}//插入单链表节点node *insert(node *head, int num){node *p1, *p2, *p3;p2 = head;p1 = (node *)malloc(sizeof(node));//先将要插入的节点申请下~p1->data = num;//思路同删除节点操作while(num > p2->data && p2->next != NULL) {p3 = p2;p2 = p2->next;}if(num <= p2->data){if(head == p2){p1->next = p2;head = p1;}else{p3->next = p1;p1->next = p2;}}else {//此处要细心,别忘了可以在尾部插入节点。p2->next = p1;p1->next = NULL;}return head;}//单链表排序node *sort(node *head){node *p;p = head;int n, temp;n = length(head);//运用冒泡排序似乎对链表排序来说是最方便的~(递增)for(int j = 1; j < n; j++){p = head;for(int i = 0; i < n - j; i++){if(p->data > p->next->data){temp = p->data;p->data = p->next->data;p->next->data = temp;}p = p->next;}}printf("\nAfter sorting :\n");return head;}//单链表逆置node *reverse(node *head){node *p1, *p2, *p3;p1 = head;p2 = p1->next;if(head == NULL || head->next == NULL) return head;//逆置的思想:选出三个节点(第三个可能是NULL),操作前两个节点,使其逆置,而后按同样做法操作第二、三个节点和第四个节点~while(p2 != NULL){p3 = p2->next;p2->next = p1;p1 = p2;p2 = p3;}head->next = NULL;head = p1;printf("\nAfter reversing :\n");return head;}int main(){node *head;//创建单链表head = create();print(head);//排序head = sort(head);print(head);//删除单链表int numD;printf("\nInput the value you want to delete : ");scanf("%d", &numD);head = del(head, numD);print(head);//插入单链表int numS;printf("\nInput the value you want to insert : ");scanf("%d", &numS);head = insert(head, numS);print(head);//逆置head = reverse(head);print(head);system("pause");return 0;}

结果显示:




0 0