查找运算(带头结点)
(1)按结点序号查找
在单链表中要查找第 i 个结点,就必须从链表的第1个结点(开始结点,序号为1)开始,序号为0的是头结点,p指向当前结点,j为计数器,初始值为1,当p扫描下一个结点时,计数器加1。当 j=i 时,指针 p 指向的节点就是要找的节点。
/*
按节点序号查找
head 为带头节点的单链表的头指针, i为要查找的节点序号
*/
ListNode * GetNodeI(LinkList head, int i){
ListNode *p; int j;
p = head->next; j=1; //使P指向第一个节点,j=1
while(p != NULL && j < i){ //指针向后查找,直到p指向第I个结点或P为空为止
p = p->next; ++j;
}
if(j == i)
return p;
else
return NULL;
}
(2)按结点值查找
单链表中按结点值查找结点,就是从链表的开始结点出发,沿着链逐个将结点的值和给定值k进行比较,相等则返回该节点的存储位置,否则返回NULL
/*
按节点值查找
head 为带头节点的单链表的头指针,k为要查找的节点值
*/
ListNode * GetNodeK(LinkList head, DataType k){
ListNode * p = head->next; //p指向开始结点
while (p && p->data != k) //循环到p等于NULL,或p->data等于k为止
{
p = p->next; //指针指向下一个结点
}
return p; //若存在值为k的节点,则p指向该节点,否则p为NULL
}
插入运算
插入运算是将值为 x 的新结点插入到表的第 i 个结点的位置上,即插入到 ai-1 和 ai 之间。链表插入时不需要移动结点,需要移动指针进行位置查找。
思想:先使 p 指向 ai-1 的位置,然后生成一个数据域为 x 的新结点 *s,再进行插入运算
/*
插入运算
再第i个结点的位置上,插入一个数据域值为x的新结点
*/
void InsertList(LinkList head,int i,DataType x){
ListNode *p, *s; int j;
p = head; j = 0;
while (p != NULL && j < i-1)
{
p = p->next; j++; //使p指向第i-1个结点
}
if (p == NULL)
{
printf("ERROR\n");
return;
}
else
{
s = malloc(sizeof(ListNode)); //申请新结点
s->data = x;
s->next = p->next;
p->next = s;
}
}
删除运算
删除运算就是将链表的第 i 个结点从表中删去。由于第 i 个结点的存储地址是存储在第 i-1 个结点的指针域 next 中,因此要先使 p 指向第 i-1 个结点,然后使得 p->next 指向第 i+1 个结点,再将第 i 个结点释放掉。
/*
删除运算
删除第i个结点
*/
DataType DeleteList(LinkList head,int i){
ListNode *p, *s;
DataType x; int j;
p = head; j = 0;
while (p != NULL && j < i-1) //使p指向第i-1个结点
{
p = p->next; ++j;
}
if (p == NULL)
{
printf("为止错误\n");
exit(0);
}
else
{
s = p->next; //s指向第i个结点
p->next = s->next; //使p指向第i+1个结点
x = s->data; //保存被删除结点的值
free(s); //释放第i个结点
return x;
}
}
mian函数中执行
#include <stdio.h>
#include "E:/Dev/C/DataStructure/chapter2/LinkList.c"
void printLink();
int main(){
int arr[] = {1,2,3};
LinkList head = NULL;
printf("尾插法建立带头结点单链表\n");
head = CreateListR1(arr,3);
ListNode *p = GetNodeI(head,3);
int data = p->data;
printf("按结点序号查找:第三个 = %d\n",data);
ListNode *k = GetNodeK(head,2);
printf("按结点值查找:值为2的节点 = %p\n",k);
printf("插入运算:在第3个位置上插入值为33的结点\n");
InsertList(head,3,33);
printLink(head);
int x = DeleteList(head,3);
printf("删除运算:删除第三个位置上的值 = %d\n",x);
printLink(head);
return 0;
}
/*
打印输出链表
*/
void printLink(LinkList head){
printf("打印链表:");
LinkList temp = head;
while(temp != NULL){
// printf("%d ",temp->data);
printf("data=%d p=%p\n",temp->data,temp);
temp = temp->next;
}
printf("\n");
}
执行结果:
尾插法建立带头结点单链表:
data=1 p=0000000000176B70
data=2 p=0000000000176B90
data=3 p=0000000000176BB0
按结点序号查找:第三个 = 3
按结点值查找:值为2的节点 = 0000000000176B90
插入运算:在第3个位置上插入值为33的结点
打印链表:data=1512480 p=0000000000176B50
data=1 p=0000000000176B70
data=2 p=0000000000176B90
data=33 p=0000000000176BD0
data=3 p=0000000000176BB0
删除运算:删除第三个位置上的值 = 33
打印链表:data=1512480 p=0000000000176B50
data=1 p=0000000000176B70
data=2 p=0000000000176B90
data=3 p=0000000000176BB0
例子
1、试写一个算法,将一个头结点指针为a的带头节点的单链表A分解成两个单链表A和B,其中头结点指针分别为a和b,使A链表中含有原链表中序号为奇数的元素,而B链表含有原链表中序号为偶数的元素,并保持原来的相对顺序。
/*
返回新链表的头指针
*/
void Test1(LinkList a, LinkList b){
ListNode *p, *ra, *rb; //声明新结点和a,b的尾指针
ra = a;
rb = b;//尾指针指向头结点
p = a->next;//p指向第一个节点
int i = 1;
while (p != NULL)
{
if (i % 2 == 0)
{
rb->next = p; //新结点放到尾结点后面
rb = p; //尾结点指向新结点
}
else
{
ra->next = p; //将新结点放到尾结点后面
ra = p; //尾结点移动到新结点
}
++i;
p = p->next;
}
ra->next = rb->next = NULL;
}
执行:
int main(){
int arr[] = {1,2,3,4,5,6,7,8,9,10};
LinkList a = CreateListR1(arr,10);
printf("初始A:\n");
printLink(a);
LinkList b = (ListNode *)malloc(sizeof(ListNode));//申请B的头结点
Test1(a, b);
printf("分割后A:\n");
printLink(a);
printf("分割后B:\n");
printLink(b);
return 0;
}
结果:
初始A:
12540800 1 2 3 4 5 6 7 8 9 10
分割后A:
12540800 1 3 5 7 9
分割后B:
12522880 2 4 6 8 10
2、假设头指针为La和Lb的单链表(带头结点)分别为线性表A和B的存储结构,两个链表都是按结点数据值递增有序的。试写一算法,将这两个单链表合并为一个有序链表Lc。
/*
合并有序链表
*/
LinkList Test2(LinkList La, LinkList Lb){
ListNode *pc, *pa, *pb;//声明3个尾指针
pa = La->next;
pb = Lb->next; //尾指针指向头结点
// LinkList Lc = (ListNode *)malloc(sizeof(ListNode)); //声明C的头结点指针
LinkList Lc;
pc = Lc = La; //用A的头结点作为C的头结点。节省空间
while (pa!= NULL && pb != NULL)
{
if (pa->data <= pb->data)
{
pc->next = pa;//将pa添加到尾指针后面
pc = pa; //将C的尾指针指向pa
pa = pa->next; //下一个循环
}else
{
pc->next = pb;
pc = pb;
pb = pb->next;
}
}
pc->next = pa==NULL? pb : pa;//拼接剩余部分
return Lc;
}
执行:
int main(){
int arr[] = {1,2,3,4,5,6,7,8,9,10,15,21,55};
LinkList a = CreateListR1(arr,13);
printf("初始A:\n");
printLink(a);
LinkList b = (ListNode *)malloc(sizeof(ListNode));//申请B的头结点
Test1(a, b);
printf("分割后A:\n");
printLink(a);
printf("分割后B:\n");
printLink(b);
LinkList c = Test2(a,b);
printf("拼接后:\n");
printLink(c);
return 0;
}
结果:
初始A:
11230080 1 2 3 4 5 6 7 8 9 10 15 21 55
分割后A:
11230080 1 3 5 7 9 15 55
分割后B:
0 2 4 6 8 10 21
拼接后:
11230080 1 2 3 4 5 6 7 8 9 10 15 21 55