单链表的创建——合并链表、逆序输出
▍抽象数据结构的定义
ADT List{
数据对象:D={ai|ai∈ElemType,i=1,2…,n,n≥0}
数据关系:R1={<a(i-1),ai>| a(i-1),ai∈D,i=1,2…,n }
基本操作:
ListInit(&L)
操作结果:构造一个链表,分配内存空间,输入数据元素,按元素值递增有序输入。
ListMerge(n1, n2)
初始条件:链表已存在。
操作结果:按照原始输入的递增顺序将n1,n2进行递增排序合并。
ListReserve(n1)
初始条件:线性表已存在。
操作结果:逆序存储链表,得到递减序列。
}ADT List
▍代码
#pragma once
//List.h
typedef int Status;
typedef float Etype;
typedef int Length;
typedef struct Lnode { //结点类型
Etype data;
struct Lnode* next;
}Lnode, * nodeptr;
nodeptr ListInit(Length l); //链表初始化
nodeptr ListMerge(nodeptr n1, nodeptr n2);//链表合并
nodeptr ListReserve(nodeptr n1); //链表逆序
void Print(nodeptr n); //链表输出
void FReeList(nodeptr n); //链表空间释放
//Linklist.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
#include"List.h"
using namespace std;
Length len1, len2; //链表长度
//链表初始化——尾插法输入数据元素,按元素值递增有序输入
nodeptr ListInit(Length l) {
nodeptr L, newnode; //创建遍历使用的结点L和新结点newnode
Etype val; //输入的数据元素
nodeptr n = (nodeptr)malloc(sizeof(Lnode)); //给头结点分配内存空间
n->data = 0; //头结点初始化
n->next = NULL;
L = n;
for (int i = 0; i < l; i++) {
newnode = (nodeptr)malloc(sizeof(Lnode));
scanf("%f", &val);
newnode->data = val;
newnode->next = NULL;
L->next = newnode;
L = L->next;
}
L->next = NULL;
return n; //返回头结点
}
//链表合并——按照原始输入的递增顺序进行递增排序合并
nodeptr ListMerge(nodeptr n1, nodeptr n2) {
nodeptr h1 = n1, h2 = n2, h3; //头结点赋值h1,h2(为了不改变原链表),h3为新创建链表头结点,指向合并后的链表
nodeptr p1 = n1->next, p2 = n2->next; //遍历使用结点
//给新链表头结点分配空间并初始化
h3 = (nodeptr)malloc(sizeof(Lnode));
h3->data = -1;
h3->next = NULL;
nodeptr point = h3; //遍历所用结点
while (p1 && p2) {
if (p1->data <= p2->data) { //A链表元素≤B链表元素时,把A链表元素赋值给C
/*newnode = p1;*/
point->next = p1;
p1 = p1->next;
}
else { //A链表元素>B链表元素时,把B链表元素赋值给C
point->next = p2;
p2 = p2->next;
}
point = point->next;
}
point->next = p1 ? p1 : p2; //未遍历结点已按照递增排序,直接链接剩余结点即可,与以下等价
/*if (p1) {
point->next = p1;
p1 = p1->next;
point = point->next;
}
else {
point->next = p2;
p2 = p2->next;
point = point->next;
}*/
return h3;
}
//逆序函数——头插法可以逆序存储链表
nodeptr ListReserve(nodeptr n1) {
nodeptr h = (nodeptr)malloc(sizeof(Lnode)); //给逆序链表的头结点分配内存空间
//头结点初始化
h->data = -1;
h->next = NULL;
nodeptr node = n1->next; //遍历结点
while (node) {
nodeptr t = node->next; //先记录node->next,便于node指针的移动
node->next = h->next; //更改node->next
h->next = node;
node = t;
}
return h;
}
//输出单链表
void Print(nodeptr n) {
n = n->next;
for (; n != NULL; ) {
printf("%g ", n->data);
n = n->next;
}
printf("\n");
}
//释放头结点
void FReeList(nodeptr n) {
free(n);
}
int main() {
nodeptr head1 = NULL, head2 = NULL, head = NULL;
printf("【题目】对于按照元素递增有序的单链表A、B\n 完成【以递增有序合并】【以递减有序链接】操作并输出完成后的新链表C\n\n");
printf("请输入两个链表所需元素个数\nA.len=");
scanf("%d", &len1);
printf("B.len=");
scanf("%d", &len2);
printf("提示:【递增有序】输入整型/浮点型数据元素(使用空格隔开)\n");
printf("链表A:");
head1 = ListInit(len1);
printf("链表B:");
head2 = ListInit(len2);
printf("——初始化成功!\n\n A ");
Print(head1);
printf(" B ");
Print(head2);
printf("\n【以递增有序合并】\n得到的链表 ");
head = ListMerge(head1, head2);
Print(head);
printf("\n【以递减有序链接】\n得到的新链表 C ");
head = ListReserve(head);
Print(head);
FReeList(head);
FReeList(head1);
FReeList(head2);
return 0;
}
▍时间复杂度与空间复杂度分析
(A链表长度为A.len,B链表长度为B.len)
- 链表的合并(ListMerge)

链表合并时,算法的时间耗费在指针移动(对链表的遍历)上,遍历的是A链表,则算法的时间复杂度为O(A.len);
在合并时,内存空间并没有发生变化,所以算法的空间复杂读为O(1)。
- 链表的逆序(ListReserve)

采用头插法初始化链表的方法对合并好的链表进行逆序排列,算法的时间耗费在指针移动和赋值上。
则算法的时间复杂度为O(A.len+B.len);又因为每次对链表初始化都动态申请内存空间,所以算法的空间复杂读为O(A.len+B.len)。
▍实验事例验证与分析
①链表的初始化:输入数据元素个数和对应的数据元素

②查找操作

本文详细介绍了单链表的基本操作,包括链表的创建、合并及逆序输出,并提供了具体的代码实现。通过实例演示了如何按照元素递增顺序进行合并以及如何通过头插法实现链表逆序。

被折叠的 条评论
为什么被折叠?



