"单链表": 只有一个方向(下一个或上一个的指针)的链表
- 不带头结点的单链表
- 带头结点的单链表
struct Node
{
ElemType data; //保存数据元素 "数据域"
//"指针域": 关系
struct Node *next; //保存下一个数据元素的地址
//struct Node *prev; //保存上一个数据元素的地址
};
关于链表的操作分为4个部分:“增”、“删”、“改”、“查”
下面举几个例子来讲解
1. 根据用户的输入,建立一个有序链表。
"插入排序"算法
(1) 找插入位置
"遍历链表":找到链表中第一个值比待插入结点大的结点pk(以及pk前驱结点pr),
pk前面就是插入位置
pk = first;
while (pk)
{
if (pk->data > p->data)
{
//找到了
break;
}
pr = pk;
pk = pk->next;
}
(2) “插入操作”
遍历后,分两种情况:
2.1找到pk pk != NULL
(1) pk为第一个结点 pk == first
p比链表上所有结点的值都要小,
p应该加入到链表的最前面。
“头插法”
p->next = first;
first = p;
(2) pk为后面或中间的结点
pr为pk的前驱动结点,
p应该插入在pk的前面,pr的后面
p->next = pk;
pr->next = p;
2.2没找到pk pk == NULL
链表上所有结点的值都比p要小,
p应该加入到链表的末尾。
“尾插法”
last->next = p;
last = p;
2. 在链表的合适的位置新增加一个节点
把p指向的结点,加入到h指向的链表中去,
h指向的链表是数值的升序排序,希望加入p后,指向的链表仍然为升序。
NOTE:
h可能为空
函数返回值:
返回加入p后,链表的第一个结点的指针。
struct node * Add_a_Node(struct node *h, struct node *p)
{
if (h == NULL)
{
return p;
}
if (p == NULL)
{
return h;
}
//把p插入到h指向的链表中去。 “插入”
// 1. 找插入位置
// 一个一个地遍历(从第一个开始找,找到其值比p大的那个结点pk以及pk前驱结点pr)
// pk的前面就是插入位置
//
struct node *pk = NULL; //pk指向链表中第一个比p值大的结点
struct node *pr = NULL; //pr指向pk前面的那个结点
pk = h;
while (pk)
{
if (pk->data > p->data)
{
break;
}
else //pk->data <= p->data
{
pr = pk;
pk = pk->next;
}
}
// 2. 插入操作
// 前面找位置的结果,只有两种情况:
// 2.1 没找到 pk == NULL
// h指向的链表中所有结点的值都比p要小
// p应该加入到h指向的末尾。“尾插法”
// 此时, pr正好指向最后那个结点
// pr->next = p;
// 2.2 找到了
// 又分两种情况:
// pk为第一个结点: pk == h
// h指向的链表中所有结点的值都比p要大,
// p应该加入到h的前面。“头插法”
// p->next = h;
// h = p;
// pk为中间结点:
// p->next = pk;
// pr->next = p;
if (pk == NULL)
{
pr->next = p;
}
else
{
if (pk == h)
{
p->next = h;
h = p;
}
else
{
p->next = pk;
pr->next = p;
}
}
return h;
}
3. 删除链表中的一个结点
删除操作分为两个步骤:
(1) 找到要删除的结点
"遍历算法"
(2) 删除操作
(2.1) 先摘下来
(2.2) 再free
/*
delete_x:在h指向的单链表中,删除值为x的结点(假设顶多只有一个结点的值为
x)
@h: 指向链表(不一定有序)
@x: 要删除的结点的元素值
返回值:
返回删除后的链表的第一个结点的指针
*/
struct node * delete_x(struct node *h, ElemType x)
{
}
struct node * delete_all_x(struct node *h, ElemType x)
{
}
4. 找单链表中最后一个结点,返回其指针
"查找": 查找的条件,满足条件的结点
最后一个结点:
p->next == NULL
struct node * find_last(struct node *h)
{
struct node *p = h; //指向最后一个结点
if (p == NULL)
{
return NULL;
}
//while (p->next != NULL)
//{
// p = p->next;
//}
while (p)
{
if (p后面没有结点) // p->next == NULL
{
//p就是最后一个结点
return p;
}
else
{
//p后面还有结点
p = p->next;
}
}
}
5. 计算单链表中结点的数目
int get_count(struct node *h)
{
int n = 0;
while (h)
{
n++;
h = h->next;
}
}
很多时候,我们可能经常需要求一个链表中有多少个结点?
或者是求一个链表中的最后一个结点?……。
=> 通过第一个结点的指针,“遍历整个链表”
如果:用带头结点的链表,就可以轻松解决这些问题。
头结点:用来管理和唯一标识一个链表的 LinkedListWithHead
typedef int ElemType ; //数据元素的类型
//数据结点
typedef struct node
{
ElemType data; //"数据域":保存数据
struct node *next; //“指针域”: 下一个节点的地址
}Node;
//头结点
struct LinkedList
{
struct node *first;// 指向链表的第一个结点
struct node *last;// 指向链表的最后一个结点
int nodeNum; //记录链表中结点的数目
};
最后附上各链表的代码
单链表
LinkedList.c
#include "LinkedList.h"
#include <stdio.h>
#include <stdlib.h>
/*
根据用户输入的数据元素,按用户输入(输入0表示结束)的自然顺序
来建立一个单链表
1 3 5 7 9
*/
Node creat_LinkedList()
{
int d;
Node first = NULL;//指向新建立的链表的第一个结点
Node last = NULL;//指向新建立的链表的最后一个结点
Node p=NULL; //指向新建立的链表的当前结点
while(1)
{
scanf("%d",&d);
if(d==0)
{
break;
}
p=malloc(sizeof(Node));
p->data=d;
p->next=NULL;
if(first==NULL) //代表链表为空,此时用户输入第一个元素
{
first=p;
last=p;
}
else
{
last->next=p; //尾插法
last=p;
}
}
return first;
}
void print_Node(Node h)
{
Node p=h;
while(p)
{
printf("%d ",p->data);
p=p->next;
}
printf("\n");
}
/*
根据用户输入的数据元素,按用户输入(输入0表示结束)的逆序
来建立一个单链表
1 3 5 7 9
*/
Node creat_Rverse_LinkedList()
{
int d;
Node first = NULL;//指向新建立的链表的第一个结点
Node last = NULL;//指向新建立的链表的最后一个结点
Node p=NULL; //指向新建立的链表的当前结点
while(1)
{
scanf("%d",&d);
if(d==0)
{
break;
}
p=malloc(sizeof(Node));
p->data=d;
p->next=NULL;
if(first==NULL)
{
first=p;
last=p;
}
else
{
p->next=first; //头插法
first=p;
}
}
return first;
}
/*
根据用户输入的数据元素,根据用户输入(输入0表示结束),按从小到大顺序排列
来建立一个单链表
1 3 5 7 9
*/
Node creat_Order_LinkedList()
{
int d;
Node first = NULL;//指向新建立的链表的第一个结点
Node last = NULL;//指向新建立的链表的最后一个结点
Node p=NULL; //指向新建立的链表的当前结点
while(1)
{
scanf("%d",&d);
if(d==0)
{
break;
}
p=malloc(sizeof(Node));
p->data=d;
p->next=NULL;
if(first==NULL)
{
first=p;
last=p;
}
else
{
//"插入排序"
//遍历链表,找到链表中的第一个比p值大的那个结点pk(以及pk前的那个结点)
//pk的前面就是插入位置
Node pk=NULL;//指向链表中比p值大的那个结点
Node pr=NULL;//指向pk前面的那个结点
pk=first;
while(pk)
{
if(pk->data>p->data)
{
//此时找到了
break;
}
pr=pk;
pk=pk->next;
}
//遍历结束后,分两种情况
//(1)找到了
// (2)没找到
//没找到,说明当前链表所有结点的值,都是p小=>"尾插法"
if(pk==NULL)
{
last->next=p;//尾插法
last=p;
}
else
{
if(pk==first)
{
p->next=first;//头插法
first=p;
}
else
{
pr->next=p;//中间插入
p->next=pk;
}
}
}
}
return first;
}
/*
在链表的合适位置增加一个结点
把p指向的结点,加入到h指向的链表中去,
h指向的链表是数值的升序排序,希望加入p后,指向的链表仍然为升序
NOTE:
h可能为空
函数返回值:
返回加入p后,链表的第一个结点的指针
@h:指向一个升序的链表
@p:指向待插入的结点
返回值:
返回插入p后,新链表的第一个结点的指针
*/
Node Add_a_Node(Node h,Node p)
{
if(h=NULL)
return p;
if(p=NULL)
return h;
/*
把p插入到h指向的链表中去,“插入”
1、找插入位置
一个一个遍历(从第一个开始找,找到其值比p大的那个结点以及pk前驱结点pr)
pk的前面就是插入位置
*/
Node pk = NULL; //指向链表中第一个比p值大的结点
Node pr = NULL; //pr指向pk前面的那个结点
pk=h;
while(pk)
{
if(pk->data>p->data)
{
break;
}
pr=pk;
pk=pk->next;
}
//2、插入操作
/*
前面找位置的结果,只有两种情况
2.1没找到 pk=NULL
h指向的链表中所有结点的值都比p要小
此时pr正好指向最后那个结点
pr-next=p;
2.2找到了
又分两种情况
pk为第一个结点:pk==h
h指向的链表中所有结点的值都比p要大,
p应该加入到h的前面。“头插法”
p->next=h;
h=p;
pk为中间结点
p->next=pk;
pr->next=p;
*/
if(pk==NULL)
{
pr->next=p;
}
else
{
if(pk==h)
{
p->next=h;
h=p;
}
else
{
p->next=pk;
pr->next=p;
}
}
return h;
}
/*
delete_x:在h指向的单链表中,删除值为x的结点(假设顶多只有一个结点的值为x)
@h:指向链表(不一定有序)
@x:要删除的结点的元素值
返回值:
返回删除后的链表的第一个结点的指针
*/
Node delete_allx(Node h,ElemType x)
{
Node px=NULL; //指向要删除的那个结点
Node pr =NULL; //指向px前面的那个结点
Node ph=h; //每次遍历的第一个(起始)结点
if(h==NULL)
{
return h;
}
/*
删除算法:
1、先找到要删除的结点
遍历算法:
从链表的第一个结点开始,一个一个找
找到其值为x的结点px(以及px前面的那个结点pr)
2、删除
(1)先摘除下来
(2)删除free
*/
while(1)
{
px=ph;
while(px)
{
if(px->data==x)
{
break;
}
pr=px;
px=px->next;
}
if(px==NULL) //没有找到
{
return h;
}
ph=px->next;
if(px==h)
{
h=h->next;
px->next=NULL;
}
else //找到的那个结点是中间结点
{
pr->next=px->next;
px->next=NULL;
}
free(px);
px=NULL;
}
return h;
}
/*
找单链表中最后一个结点,返回其指针
*/
Node find_last(Node h)
{
Node pr = NULL;
Node px=NULL;
if(h==NULL)
{
return h;
}
px=h;
while(px)
{
pr =px;
px=px->next;
}
return pr;
}
/*
计算单链表中结点的数目
*/
int get_count(Node h)
{
int i=0;
Node px=NULL;
px=h;
while(px)
{
i++;
px=px->next;
}
return i;
}
int main()
{
Node h=creat_Order_LinkedList();
print_Node(h);
int x;
int i;
i = get_count(h);
printf("链表的数目为:%d\n",i);
scanf("%d",&x);
h=delete_allx(h, x);
printf("after linkedlist:");
print_Node(h);
h=find_last(h);
printf("after linkedlist:");
print_Node(h);
}
LinkedList.h
#ifndef __LINKEDLIST_H__
#define __LINKEDLIST_H__
typedef int ElemType; //数据元素类型
typedef struct node
{
ElemType data; //"数据域":保存数据
struct node *next; //"指针域":下一个结点的地址
}*Node;
Node creat_LinkedList();
Node Insert_A_Node();
void print_Node(Node h);
Node creat_Rverse_LinkedList();
Node creat_Order_LinkedList();
Node Add_a_Node(Node h,Node p);
Node delete_allx(Node h,ElemType x);
Node find_last(Node h);
int get_count(Node h);
#endif
带头结点的单链表
LinkedListWithHead.c
#include <stdio.h>
#include <stdlib.h>
#include "LinkedListWithHead.h"
hNode creat_order_LinkedList()
{
hNode l=malloc(sizeof(hNode));
l->first=NULL;
l->last=NULL;
l->nodeNum=0;
Node p=NULL;
int d;
while(1)
{
scanf("%d",&d);
if(d==0)
{
break;
}
p=malloc(sizeof(Node)); //创建一个结点存入用户输入的数
p->data=d;
p->next=NULL;
if(l->first==NULL&&l->last==NULL)
{
l->first=p;
l->last=p;
}
else
{
Add_a_Node(l,p);
}
}
return l;
}
/*
往“带头结点的单链表”添加一个"数据结点"
Add_a_Node:把p指向的数据结点,添加到l指向的带头结点的单链表中去
在添加p后l仍然保持升序
@l:l指向的单链表是升序
@p:
*/
void Add_a_Node(hNode l,Node p)
{
Node pk=NULL;
Node pr=NULL;
if(l->first==NULL&&l->last==NULL)
{
l->first=p;
l->last=p;
}
pk=l->first;
while(pk)
{
if(pk->data>=p->data)
{
break; //此时找到了第一个比p结点大的结点
}
pr=pk;
pk=pk->next;
}
/*
1、没有找到比p结点大的结点,利用尾插法插入
*/
if(pk==NULL)
{
l->last->next=p;
l->last=p;
}
/*
2、找到了
2.1找到的结点为第一个结点
2.2找到的结点为中间结点
*/
if(pk==l->first)
{
p->next=l->first;
l->first=p;
}
else
{
pr->next=p;
p->next=pk;
}
}
void print_list(hNode l)
{
Node pk = l->first;
while(pk)
{
printf("%d ",pk->data);
pk=pk->next;
}
printf("\n");
}
/*
在"带头结点的单链表"上删除结点
delete_all_x:在l指向的带头结点的单链表,删除值为x的所有结点
@l:
@x;
返回值:
无
*/
void delete_all_x(hNode l,ElemType x)
{
Node pk=NULL; //指向所找到的那个目标结点
Node pr=NULL; //指向目标结点的前一个结点
Node ph=l->first; //删除结点后,遍历的当前位置
if(l->first==NULL)
{
return;
}
while(1)
{
pk=ph;
while(pk)
{
if(pk->data==x) //找到了目标值
{
break;
}
pr=pk;
pk=pk->next;
}
/*
分两种情况:
1、没有找到
2、找到了
2.1找到的那个结点为第一个结点
2.2找到的那个结点为最后一个结点
2.3找到的那个结点为中加结点
*/
if(pk==NULL)
{
return;
}
ph=pk->next;
if(pk==l->first)//要删除的是第一个结点
{
if(l->first==l->last)
{
//链表中只有一个结点,并且要删除这个结点
l->first=l->last=NULL;
}
else
{
l->first=pk->next;
pk->next=NULL;
}
}
else if(pk==l->last)//要删除的是最后一个结点
{
//链表至少会有两个及以上的结点
pr->next=pk->next;
l->last=pr;
}
else
{
pr->next=pk->next;
pk->next=NULL;
}
free(pk);
pk=NULL;
}
}
int main()
{
hNode l=creat_order_LinkedList();
print_list(l);
int x;
scanf("%d",&x);
delete_all_x(l,x);
printf("after linkedlist:\n");
print_list(l);
return 0;
}
LinkedListWithHead.h
#ifndef __LINKEDLISTWITHHEAD__
#define __LINKEDLISTWITHHEAD__
typedef int ElemType;
typedef struct node
{
ElemType data; //"数据域":保存数据
struct node* next;//指针域:下一个结点的地址
}*Node;
typedef struct hnode
{
Node first; //指向链表的第一个结点
Node last; //指向链表的最后一个结点
int nodeNum; //记录链表中结点的数目
}*hNode;
hNode creat_order_LinkedList();
void Add_a_Node(hNode l,Node p);
void print_list(hNode l);
void delete_all_x(hNode l,ElemType x);
#endif
双向链表
biLinkedList.c
#include <stdio.h>
#include <stdlib.h>
#include "biLinkedList.h"
/*
创建一个双向链表
*/
hbiNode creat_biLinkedList()
{
hbiNode l = malloc(sizeof(hbiNode));
l->first=NULL;
l->last=NULL;
int d;
while(1)
{
scanf("%d",&d);
if(d==0)
{
break;
}
biNode p=malloc(sizeof(biNode));
p->data=d;
p->next=NULL;
p->prev=NULL;
if(l->first==NULL&&l->last==NULL)
{
l->first=p;
l->last=p;
}
else
{
l->last->next=p;
p->prev=l->last;
l->last=p;
}
}
return l;
}
/*
创建一个有序双向链表
*/
hbiNode creat_order_biLinkedList()
{
hbiNode l = malloc(sizeof(hbiNode));
l->first=NULL;
l->last=NULL;
int d;
while(1)
{
scanf("%d",&d);
if(d==0)
{
break;
}
biNode p=malloc(sizeof(biNode));
p->data=d;
p->next=NULL;
p->prev=NULL;
if(l->first==NULL&&l->last==NULL)
{
l->first=p;
l->last=p;
}
else
{
add_a_biNode(l, p);
}
}
return l;
}
/*
add_a_biNode:在l指向的带头结点的双向链表中,添加一个数据结点p
@l:l指向一个升序的双向链表,期望添加p后,l仍然升序
@p:待添加的数据结点
*/
void add_a_biNode(hbiNode l,biNode p)
{
if(l==NULL||p==NULL)
{
return;
}
biNode pk=NULL;//指向链表中第一个比p值大的结点
biNode pr=NULL;//指向pk前面的那个结点
pk=l->first;
while(pk)
{
if(pk->data>p->data)
{
break;
}
pr=pk;
pk=pk->next;
}
//2、插入操作
if(pk==NULL)
{
//没找到,链表上所有结点的值,都比p要小,或者
//l是一个空链表
if(l->first==NULL)
{
p->next=NULL;
p->prev=NULL;
l->first=p;
l->last=p;
}
else
{
//l不是一个空链表
l->last->next=p;
p->prev=l->last;
l->last=p;
}
}
else
{
if(pk==l->first)
{
//pk为第一个结点,头插入法
p->next=l->first;
l->first->prev=p;
l->first=p;
}
else//中间插入
{
p->next=pk;
pk->prev=p;
pr->next=p;
p->prev=pr;
}
}
}
void print_hbiLinkedList(hbiNode h)
{
if(h==NULL)
{
return;
}
biNode pk=h->first;
while(pk)
{
printf("%d ",pk->data);
pk=pk->next;
}
printf("\n");
}
/*
从双向链表中删除一个数据结点
算法:
1、找到要删除的那个结点
2、删除操作
第一个结点
中间结点
最后一个结点
*/
void delete_all_x(hbiNode l,ElemType x)
{
if(l==NULL)
{
return;
}
biNode ph = l->first;//每次遍历的起始结点
biNode pk = NULL;//指向要找的那个结点
biNode pr = NULL;//指向要找的那个结点的前一个结点
while(1)
{
pk=ph;
while(pk)
{
if(pk->data==x)
{
break;
}
pr=pk;
pk=pk->next;
}
//1、没有找到,那么直接return
if(pk==NULL)
{
return;
}
ph=pk->next;
if(pk==l->first)
{
if(l->first==l->last)
{
l->first=NULL;
l->last=NULL;
}
else
{
l->first=pk->next;
pk->next->prev=NULL;
pk->next=NULL;
}
}
else if(pk==l->last)
{
l->last=pr;
pr->next=NULL;
pk->prev=NULL;
}
else
{
pr->next=pk->next;
pk->next->prev=pr;
}
free(pk);
pk=NULL;
}
}
int main()
{
hbiNode h=creat_order_biLinkedList();
print_hbiLinkedList(h);
int x;
scanf("%d",&x);
delete_all_x(h,x);
print_hbiLinkedList(h);
return 0;
}
biLinkedList.h
#ifndef __BILINKEDLIST_H__
#define __BILINKEDLIST_H__
typedef int ElemType;
typedef struct binode
{
ElemType data;//数据域
struct binode* next;//指向后续结点
struct binode* prev;//指向前驱结点
}*biNode;
typedef struct hbinode
{
biNode first;//指向双向链表的第一个结点
biNode last;//指向双向链表的最后一个结点
}*hbiNode;
hbiNode creat_biLinkedList();
hbiNode creat_order_biLinkedList();
void add_a_biNode(hbiNode l,biNode p);
void print_hbiLinkedList(hbiNode h);
#endif
关于链表的一些习题,帮助理解
linkedlist.c
#include <stdio.h>
#include <stdlib.h>
#include "linkedlist.h"
/*
创建一个带头结点的链表
*/
hNode create_list()
{
hNode l=malloc(sizeof(hNode));
Node p =NULL;
l->first = NULL;
l->last = NULL;
l->nodeNum=0;
int d;
while(1)
{
scanf("%d",&d);
if(d==0)
{
break;
}
p = malloc(sizeof(Node));
p->data=d;
p->next=NULL;
if(l->first==NULL&&l->last==NULL)
{
l->first=p;
l->last=p;
}
else
{
l->last->next=p;
l->last=p;
}
}
return l;
}
/*
作业1:
就地逆置一个单链表(不允许申请新的结点空间)
*/
void reverse(hNode h)
{
Node pr=NULL; //pr指向链表的第一个结点
Node pk=NULL; //pk指向链表的后一个结点
Node pn=NULL; //指向pk所指向结点的后一个结点
pr=h->first;
pk=h->first->next;
pr->next=NULL;
while(pk)
{
pn = pk->next;
pk->next=pr; //因为操作的毕竟是同一个结点,那么指向的下一个结点地址可以覆盖
pr=pk;
pk=pn;
}
h->first=pr;
}
void print_list(hNode h)
{
Node pk=h->first;
while(pk)
{
printf("%d ",pk->data);
pk=pk->next;
}
printf("\n");
}
/*
创建一个带头结点的有序链表
*/
hNode creat_order_LinkedList()
{
hNode l=malloc(sizeof(hNode));
l->first=NULL;
l->last=NULL;
l->nodeNum=0;
Node p=NULL;
int d;
while(1)
{
scanf("%d",&d);
if(d==0)
{
break;
}
p=malloc(sizeof(Node)); //创建一个结点存入用户输入的数
p->data=d;
p->next=NULL;
if(l->first==NULL&&l->last==NULL)
{
l->first=p;
l->last=p;
}
else
{
Add_a_Node(l,p);
}
}
return l;
}
/*
往“带头结点的单链表”添加一个"数据结点"
Add_a_Node:把p指向的数据结点,添加到l指向的带头结点的单链表中去
在添加p后l仍然保持升序
@l:l指向的单链表是升序
@p:
*/
void Add_a_Node(hNode l,Node p)
{
Node pk=NULL;
Node pr=NULL;
if(l->first==NULL&&l->last==NULL)
{
l->first=p;
l->last=p;
}
pk=l->first;
while(pk)
{
if(pk->data>=p->data)
{
break; //此时找到了第一个比p结点大的结点
}
pr=pk;
pk=pk->next;
}
/*
1、没有找到比p结点大的结点,利用尾插法插入
*/
if(pk==NULL)
{
l->last->next=p;
l->last=p;
}
/*
2、找到了
2.1找到的结点为第一个结点
2.2找到的结点为中间结点
*/
if(pk==l->first)
{
p->next=l->first;
l->first=p;
}
else
{
pr->next=p;
p->next=pk;
}
}
/*
归并两个有序链表,要求归并后的链表也有序
*/
hNode merger(hNode ha,hNode hb)
{
Node pk = NULL; //指向hb链表的第一个结点
Node pn = NULL; //指向pk的后一个结点
pk=hb->first;
while(pk)
{
pn=pk->next;
pk->next=NULL;//此时摘除了pk指向的那个结点
Add_a_Node(ha,pk);
pk=pn;
}
return ha;
}
/*
返回一个链表倒数第k个结点的指针
算法:
拍两个哨兵 p1 p2
p2先走k步,p1和p2间隔k个结点
然后p1和p2同时移动一个结点,直到
p2为NULL,p1就是倒数第k个结点
*/
int bottom(hNode h,int k)
{
Node p1=NULL;//哨兵1
Node p2=NULL;//哨兵2
p1=h->first;
p2=h->first;
for(int i=0;i<k;i++)
{
p2=p2->next; //此时p2与p1指向的位置间隔k个结点
}
while(p2)
{
p1=p1->next; //此时p1指向的结点就是链表倒数第k个结点的指针
p2=p2->next;
}
return p1->data;
}
/*
返回一个链表中间结点的指针(只遍历一次)
算法:
派两个哨兵p1,p2
每次p1走一步,p2走两步
......
当p2为NULL的时候,p1就是在链表的中间结点
*/
int find_middle(hNode h)
{
Node p1=NULL;
Node p2=NULL;
p1=h->first;
p2=h->first;
while(p2)
{
if(p2->next)
{
p1=p1->next;
p2=p2->next->next;
}
else
{
break;
}
}
return p1->data;
}
/*
请设计一个算法,判断一个单链表中是否存在环
算法:
两个哨兵,一个跑两步,一个跑一步
如果有环,最终会相遇
如果没有环,跑得快的那个最终会到NULL
*/
int is_circle(hNode h)
{
Node pa=h->first;
Node pb=h->first;
while(pb)
{
pa=pa->next;
if(pb->next)
{
pb=pb->next->next;
}
else
{
pb=NULL;
}
if(pa==pb)
{
return 1; //有环
}
if(pb==NULL)
{
return 0; //没有环
}
}
}
/*
删除单链表(无序的)中最小值结点(假设最小值唯一)
*/
void delete_min(hNode h)
{
Node pmin=NULL; //指向最小值结点
Node pmin_r=NULL; //指向最小值结点的前一个结点
pmin=h->first;
Node pk=h->first;
Node pr=NULL;
while(pk)//遍历链表
{
if(pk->data<pmin->data)
{
pmin_r=pr;
pmin=pk;
}
pr=pk;
pk=pk->next;
}
//2、分情况去删除
if(pmin==h->first)
{
h->first=pmin->next;
pmin->next=NULL;
}
else if(pmin==h->last)
{
pmin_r->next=NULL;
h->last=pmin_r;
}
else
{
pmin_r->next=pmin->next;
pmin->next=NULL;
}
free(pmin);
pmin=NULL;
}
/*
逆序存储整数的各个位,并实现两个整数相加
"大数相加另外一个实现方式"
*/
hNode creat_list_by_digtal(int num)
{
hNode l = malloc(sizeof(hNode));
l->first=NULL;
l->last=NULL;
Node p= NULL;
while(num)
{
p=malloc(sizeof(Node));
p->data=num%10;
p->next=NULL;
num=num/10;
if(l->first==NULL)
{
l->first=p;
l->last=p;
}
else
{
l->last->next=p;
l->last=p;
}
}
return l;
}
hNode add_two_numbers(hNode ha,hNode hb)
{
hNode hc = malloc(sizeof(hNode));
hc->first=NULL;
hc->last=NULL;
Node h1 = ha->first;
Node h2 = hb->first;
Node p = NULL;
int a,b; //ha的当前位a,hb的当前位b
int c=0;
int sum=0;
while(h1||h2)
{
a=h1?h1->data:0;
b=h2?h2->data:0;
sum=a+b+c;
c=sum/10;
p=malloc(sizeof(Node));
p->data=sum%10;
p->next=NULL;
if(hc->first==NULL)
{
hc->first=p;
hc->last=p;
}
else
{
hc->last->next=p;
hc->last=p;
}
h1=h1?h1->next:NULL;
h2=h2?h2->next:NULL;
}
if(c) //存取最高为的c,有可能最后ha,hb的值都加完了,但还是存在进位,这个时候还需要保存一下
{
p=malloc(sizeof(Node));
p->data=c;
p->next=NULL;
hc->last->next=p;
hc->last=p;
}
return hc;
}
/*
链表A,B,判断A是否为B的子序列(子序列:连续的一部分)
int is_sequence(hNode ha,hNode hb)
返回值:1 是子序列
返回值:0 不是子序列
*/
int is_sequence(hNode ha,hNode hb)
{
if(ha==NULL||hb==NULL)
{
return 0;
}
Node h1=ha->first;
Node h2=hb->first;
while(h2)
{
if(h2->data==h1->data)
{
while(h1&&h2)
{
if(h1->data!=h2->data)
{
break;
}
h1=h1->next;
h2=h2->next;
}
if(h1==NULL)
{
return 1;
}
}
h2=h2->next;
}
return 0;
}
/*
链表A存的是整数,有正有负,写一个程序,把链表A中的负数,放在前面
算法:
遍历链表找到第一个负数的结点px
摘下来,然后用"头插法"加到原链表
优化:
先遍历链表找到第一个正数结点,pk
从pk后面遍历找到第一个负数结点px
然后摘下来,加到pk的前面(也可用"头插法"加入原链表)
*/
void neg_head(hNode h)
{
Node pk=h->first;
Node px=NULL;
Node pr = NULL; //指向px所指向结点的前一个结点
Node pn = NULL;//指向px所指向结点的后一个结点
while(pk)
{
if(pk->data>0) //找到链表中的第一个正数
{
break;
}
pk=pk->next;
}
pr=pk;
px=pk->next;
while(px)
{
pn=px->next;
if(px->data<0)
{
pr->next=px->next;
px->next=h->first;
h->first=px;
}
pr=px;
px=pn;
}
}
/*
对一个链表进行排序
算法:
新链表:开始为空,从原链表中一个一个把结点摘下来
然后再按"插入排序"算法把结点,加入到新链表中去
*/
void sort_list(hNode h)
{
Node pk=h->first->next; //产生的问题:再开辟一个新的链表空间去存储排序后的结点
h->first->next=NULL; //出现了问题,就会一直陷入死循环,一直动态开辟,存储那同一个结点的值
h->last=h->first;
Node pn=NULL;
while(pk)
{
pn=pk->next;
pk->next=NULL;
Add_a_Node(h, pk);
pk=pn;
}
}
/*
设A,B为单链表,且A,B都为升序,求A、B的交集
要求:
重新组成一个新链表C,原链表A,B不动
要求C也是升序
(1)A,B本身无重复
(2)A,B本身可能有重复,要求C无重复
*/
hNode get_intersection(hNode ha,hNode hb)
{
hNode hc = malloc(sizeof(hNode));
hc->first=NULL;
hc->last=NULL;
Node h1=ha->first;
Node h2=hb->first;
Node temp=NULL;
Node h = malloc(sizeof(Node));
while(1)
{
if(h1==NULL||h2==NULL)
{
break;
}
if(h1->data>h2->data)
{
h2=h2->next;
}
else if(h1->data<h2->data)
{
h1=h1->next;
}
else
{
if(temp==NULL||(temp->data!=h2->data))
{
h->data=h2->data;
h->next=NULL;
if(hc->first==NULL&&hc->last==NULL)
{
hc->first=h;
hc->last=h;
temp=h;
}
else
{
hc->last->next=h;
hc->last=h;
temp=h;
}
}
h1=h1->next; //在相等的时候,要同时往后挪一格,要不然总是跳进这个循环
h2=h2->next;
}
}
return hc;
}
int main()
{
hNode ha=create_list();
hNode hb = create_list();
hNode hc = get_intersection(ha,hb);
print_list(hc);
return 0;
}
linkedlist.h
#ifndef __LINKEDLIST_H__
#define __LINKEDLIST_H__
typedef int ElemType;
typedef struct node
{
ElemType data; //"数据域":保存数据
struct node* next;//指针域:下一个结点的地址
}*Node;
typedef struct hnode
{
Node first; //指向链表的第一个结点
Node last; //指向链表的最后一个结点
int nodeNum; //记录链表中结点的数目
}*hNode;
hNode create_list();
void reverse(hNode h);
void print_list(hNode h);
hNode creat_order_LinkedList();
void Add_a_Node(hNode l,Node p);
hNode merger(hNode ha,hNode hb);
hNode creat_order_LinkedList();
/*
返回一个链表倒数第k个结点的指针
算法:
拍两个哨兵 p1 p2
p2先走k步,p1和p2间隔k个结点
然后p1和p2同时移动一个结点,直到
p2为NULL,p1就是倒数第k个结点
*/
int bottom(hNode h,int k);
/*
返回一个链表中间结点的指针(只遍历一次)
算法:
派两个哨兵p1,p2
每次p1走一步,p2走两步
......
当p2为NULL的时候,p1就是在链表的中间结点
*/
int find_middle(hNode h);
/*
请设计一个算法,判断一个单链表中是否存在环
算法:
两个哨兵,一个跑两步,一个跑一步
如果有环,最终会相遇
如果没有环,跑得快的那个最终会到NULL
*/
int is_circle(hNode h);
/*
删除单链表(无序的)中最小值结点(假设最小值唯一)
*/
void delete_min(hNode h);
/*
逆序存储整数的各个位,并实现两个整数相加
"大数相加另外一个实现方式"
*/
hNode creat_list_by_digtal(int num);
hNode add_two_numbers(hNode ha,hNode hb);
/*
链表A,B,判断A是否为B的子序列(子序列:连续的一部分)
int is_sequence(hNode ha,hNode hb)
返回值:1 是子序列
返回值:0 不是子序列
*/
int is_sequence(hNode ha,hNode hb);
/*
链表A存的是整数,有正有负,写一个程序,把链表A中的负数,放在前面
算法:
遍历链表找到第一个负数的结点px
摘下来,然后用"头插法"加到原链表
优化:
先遍历链表找到第一个正数结点,pk
从pk后面遍历找到第一个负数结点px
然后摘下来,加到pk的前面(也可用"头插法"加入原链表)
*/
void neg_head(hNode h);
/*
对一个链表进行排序
算法:
新链表:开始为空,从原链表中一个一个把结点摘下来
然后再按"插入排序"算法把结点,加入到新链表中去
*/
void sort_list(hNode h);
/*
设A,B为单链表,且A,B都为升序,求A、B的交集
要求:
重新组成一个新链表C,原链表A,B不动
要求C也是升序
(1)A,B本身无重复
(2)A,B本身可能有重复,要求C无重复
*/
hNode get_intersection(hNode ha,hNode hb);
#endif