数据结构学习~03.关于单链表的归并以及头尾插入问题
本文是上一篇文章的后续,详情点击该链接~
问题: 设有A和B两个带头结点并且元素递增有序的单链表,我们设计一个算法,将A和B归并成一个按元素值非递减有序的单链表C。而C的话呢,它是由A和B这两个链表中的结点组成。
关于这个问题呢,我们可以先来分析分析。假如说,我要使归并后的C中元素也井然有序,我们可以从A和B中挑出最小的元素插入到C的尾部,这样一来A和B的所有元素都插入C之后,C是肯定递增有序的。
那么问题来了?哪一个元素是A和B中最小的元素呢???其实也很明显,因为A和B都是递增的,所以最小的元素肯定是开始结点的那个元素。
由于做这个题目的前提呢,我们得先有一个单链表增加和遍历,才能做这道题。所以我们先来写一个单链表的增加和遍历。
#include<stdio.h>
#include<stdlib.h>
typedef struct no_de {
int data; //data中存放数据域
struct no_de* next; //指向后继结点的指针
}Node;
//创建链表头节点,并初始化
Node* getHead();
//插入链表元素到尾部
void Insert(Node *list,int data);
//遍历链表
void Print(Node *list);
Node* getHead() {
Node* head = (Node*)malloc(sizeof(Node));
head->data = NULL;
head->next = NULL;
return head;
}
void Insert(Node *list, int data) {
Node* node = (Node*)malloc(sizeof(Node));
node->data = data;
node->next = list->next;
list->next = node;
}
void Print(Node* list) {
//如果链表是空就打印 []
if (list->next == NULL || list == NULL) {
printf("[ ]");
return;
}
Node* node = list->next;
printf("[ ");
while (node != NULL) {
//判断是否到了最后一个元素
if (node->next != NULL) {
printf("%d ,",node->data);
}
else {
//到了最后一个元素的时候就不加 ,
printf("%d ]", node->data);
}
node = node->next;
}
printf("\n");
}
int main(int argc,char*argv[]) {
Node* list = getHead();
for (int i = 1; i < 10; i++) {
Insert(list, i);
}
Print(list);
getchar();
}
运行一下,目前是没有什么问题的。
那么现在呢,我们就来研究一下刚刚说的那个归并问题
void merge(Node *A,Node *B,Node *&C) {
//用amin来跟踪A的最小值结点
Node* amin = A->next;
//用bmin来跟踪B的最小值结点
Node* bmin = B->next;
//用last始终指向C的终端结点
Node* last;
//用A的头节点来做C的头结点
C = A;
C->next = NULL;
//B的头节点无用,所以我们释放它
free(B);
//last指向C,此时头节点指向终端结点
last = C;
//当amin和bmin都不为空的时候,我们就取较小的结点插入到C的尾部
while (amin != NULL && bmin != NULL) {
if (amin->data <= bmin->data) {
last->next = amin;
amin = amin->next;
last = last->next;
}
else {
last->next = bmin;
bmin = bmin->next;
last = last->next;
}
}
//这里还需要注意,A和B两个元素有可能会出现这种情况
//比如A的元素已经全部插入到C中,而B还没有插完。或者B插完A没插完
//这个时候没插完的元素基本上是大于C中的元素。所以这里呢
//这两个if就是解决这个问题的。
if (amin != NULL) {
last->next = amin;
}
if (bmin != NULL) {
last->next = bmin;
}
}
刚才的操作呢,主要是涉及到了尾插法建立单链表,还有就是归并这两个知识点。那么现在呢我们有n个元素已经存到了数组中,我们又怎么把它插入到链表中呢?
尾插法
void CreateListR(Node*& C, int* a, int n) {
//s用来指向新申请的结点,r指向C的终端结点
Node* s, * r;
int i;
//申请C的头节点空间
C = getHead();
//r指向头节点,这个时候头结点就是终端结点
r = C;
for (i = 0; i < n; ++i) {
//s指向新申请的结点
s = getHead();
//新申请的结点来接收a中的一个元素
s->data = a[i];
//用r来接纳新结点
r->next = s;
//r指向终端结点,以便于接纳下一个到来的结点
r = r->next;
}
r->next = NULL;
}
头插法
void CreateListF(Node*& C, int* a, int n) {
Node* s; int i;
C = getHead();
for (i = 0; i < n; ++i) {
s = (Node*)malloc(sizeof(Node));
s->data = a[i];
//s所指新结点的指针域 next 指向C中的开始结点
s->next = C->next;
//头结点的指针域next指向s结点,使得s成为新的开始结点
C->next = s;
}
}
奉上完整代码
#include<stdio.h>
#include<stdlib.h>
typedef struct no_de {
int data; //data中存放数据域
struct no_de* next; //指向后继结点的指针
}Node;
//创建链表头节点,并初始化
Node* getHead();
//插入链表元素到尾部
void Insert(Node *list,int data);
//遍历链表
void Print(Node *list);
//尾插归并
void merge(Node* A, Node* B, Node*& C);
//数组尾插链表
void CreateListR(Node *&C,int *a,int n);
//数组头插链表
void CreateListF(Node*& C, int* a, int n);
void CreateListF(Node*& C, int* a, int n) {
Node* s; int i;
C = getHead();
for (i = 0; i < n; ++i) {
s = (Node*)malloc(sizeof(Node));
s->data = a[i];
//s所指新结点的指针域 next 指向C中的开始结点
s->next = C->next;
//头结点的指针域next指向s结点,使得s成为新的开始结点
C->next = s;
}
}
void CreateListR(Node*& C, int* a, int n) {
//s用来指向新申请的结点,r指向C的终端结点
Node* s, * r;
int i;
//申请C的头节点空间
C = getHead();
//r指向头节点,这个时候头结点就是终端结点
r = C;
for (i = 0; i < n; ++i) {
//s指向新申请的结点
s = getHead();
//新申请的结点来接收a中的一个元素
s->data = a[i];
//用r来接纳新结点
r->next = s;
//r指向终端结点,以便于接纳下一个到来的结点
r = r->next;
}
r->next = NULL;
}
void merge(Node *A,Node *B,Node *&C) {
//用amin来跟踪A的最小值结点
Node* amin = A->next;
//用bmin来跟踪B的最小值结点
Node* bmin = B->next;
//用last始终指向C的终端结点
Node* last;
//用A的头节点来做C的头结点
C = A;
C->next = NULL;
//B的头节点无用,所以我们释放它
free(B);
//last指向C,此时头节点指向终端结点
last = C;
//当amin和bmin都不为空的时候,我们就取较小的结点插入到C的尾部
while (amin != NULL && bmin != NULL) {
if (amin->data <= bmin->data) {
last->next = amin;
amin = amin->next;
last = last->next;
}
else {
last->next = bmin;
bmin = bmin->next;
last = last->next;
}
}
//这里还需要注意,A和B两个元素有可能会出现这种情况
//比如A的元素已经全部插入到C中,而B还没有插完。或者B插完A没插完
//这个时候没插完的元素基本上是大于C中的元素。所以这里呢
//这两个if就是解决这个问题的。
if (amin != NULL) {
last->next = amin;
}
if (bmin != NULL) {
last->next = bmin;
}
}
Node* getHead() {
Node* head = (Node*)malloc(sizeof(Node));
head->data = NULL;
head->next = NULL;
return head;
}
void Insert(Node *list, int data) {
Node* node = (Node*)malloc(sizeof(Node));
node->data = data;
node->next = list->next;
list->next = node;
}
void Print(Node* list) {
//如果链表是空就打印 []
if (list->next == NULL || list == NULL) {
printf("[ ]");
return;
}
Node* node = list->next;
printf("[ ");
while (node != NULL) {
//判断是否到了最后一个元素
if (node->next != NULL) {
printf("%d ,",node->data);
}
else {
//到了最后一个元素的时候就不加 ,
printf("%d ]", node->data);
}
node = node->next;
}
printf("\n");
}
int main(int argc,char*argv[]) {
Node* A = getHead();
Node* B = getHead();
//===============================
printf("链表1: ");
for (int i = 1; i < 10; i++) {
Insert(A, (i * 2));
}
Print(A);
//===============================
printf("链表2: ");
for (int i = 1; i < 10; i++) {
Insert(B, (i * 3));
}
Print(B);
//===============================
printf("合并后: ");
Node* C = getHead();
merge(A,B,C);
Print(C);
//=============数组尾插================
printf("数组尾插到链表");
Node* P = getHead();
int arr[10] = {1,3,5,7,9,2,4,6,8,10};
CreateListR(P,arr,10);
Print(P);
//============数组头插=================
printf("数组头插到链表");
Node* K = getHead();
CreateListF(K, arr,10);
Print(K);
getchar();
}
至于归并呢,我们刚才用的是尾插法,头插法的话,我们就下期再聊吧~