数据结构学习~03.关于单链表的归并以及头尾插入问题

数据结构学习~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();
}

       至于归并呢,我们刚才用的是尾插法,头插法的话,我们就下期再聊吧~

猜你喜欢

转载自blog.csdn.net/qq_41424688/article/details/107354103