C语言实现单链表的快速排序算法

 更新时间:2022年01月20日 09:52:20   作者:CUP-GYC  
大家好,本篇文章主要讲的是C语言实现单链表的快速排序算法,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下

背景

传统QuickSort算法最大不足之处在于,由于其基于可索引存储结构设计(一般为数组或索引表),因而无法用于链式存储结构,而链式存储结构的实际应用非常广泛,例如动态存储管理、动态优先级调度等等,故本文针对于单向链表,以QuickSort的分治策略为基础,提出一种可用于单向链表的快速排序算法。

设计思路

将单向链表的首节点作为枢轴节点,然后从单向链表首部的第二个节点开始,逐一遍历所有后续节点,并将这些已遍历节点的key与枢轴节点的key进行比较,根据比较结果,重新将这些节点链接为less和more两个单向链表,less中所包含节点的key均小于枢轴节点的 key;more中所包含节点的key均大于或等于枢轴节点的key。然后再对得到的两个单链表进行递归操作,将进行内部递归排序最后连接到枢轴上。同时为达到在每次划分后将规模更小的两个单向链表链接到枢轴节点上,必须记录各自的尾节点位置,即需要设置两个指向尾节点的指针。less链表的首节点指针设定为lessHead,尾节点指针设定为lessTail;more链表的首节点指针设定为 moreHead,尾节点指针设定为moreTail。当前正在遍历的节点指针设定为current。当单向链表遍历结束后,亦即完成了一趟划分, 如此递归进行,便可完成整个单向链表的排序。除此之外,为了简化将 less和more单向链表链接到枢轴节点前、后部的过程,还需设定两个指向单向链表尾节点的指针 lessTail和moreTail。在递归过程中,得到的单链表的长度会依次减小,直到长度减小到一的时候即为递归出口。

算法主要步骤

步骤1:算法接收两个指针,其中listHead指 向单向链表首节点,listTail为空指针,划分过程中,listTail将指向单向链表的尾节点,划分后用于链接less单向链表到枢轴节点前。
步骤2:如果单向链表listHead仅有一个节点,则说明已有序,本层递归结束,返回listHead。
步骤3:令lessHead、lessTail、moreHead和 moreTail为空,令current为listHead的next域,即单向链表的第二个节点。
步骤4:如果current节点为空,则转入步骤13。
步骤5:如果current节点的key小于枢轴节点(即listHead)的key,则current节点应链接到 less单向链表,转入步骤6;否则,current节点应链 接到more单向链表,转入步骤9。
步骤6:修改lessTail指针使其指向current 节点。如果lessHead为空,则转入步骤7;否则转入步骤8。
步骤7:将less节点链接为单链表的首节点。
步骤8:将current节点链接为less单链表的尾结点;
步骤9:修改moreTail指针使其指向current 节点。如果moreHead为空,则转入步骤10;否则转入步骤11。
步骤10:将currnet节点链接为more单向链表的首节点。
步骤11:将currnet节点链接为more单向链表的尾节点。
步骤12:将current节点移动到单向链表的下一个节点。
步骤13:如果more单向链表不为空,则转入步骤14;否则转入步骤18。
步骤14:标记more单向链表的结束位置,即 置moreTail的next域为空。
步骤15:递归调用本算法,继续划分more单 向链表,传人moreHead和moreTail。步骤16将经过递归排序的more单向链表 链接到枢轴节点后。
步骤17:修改listTail指针使其指向more— Tail,以便本层递归结束后供上层递归过程使用。
步骤18:由于more单向链表为空,则枢轴节 点便是尾节点,即置listHead的next域为空。 步骤19:修改listTail指针使其指向listHead。
步骤20:如果less单向链表不为空,则转入 步骤21;否则转入步骤24。
步骤21:标记less单向链表的结束位置,即 置lessTail的next域为空。
步骤22:递归调用本算法,继续划分less单 向链表,传人lessHead和lessTail。 步骤23将经过递归排序的less单向链表链 接到枢轴节点前。
步骤24:由于less单向链表为空,则枢轴节 点便是首节点,即置lessHead为listHead。
步骤25:本层递归结束,返回lessHead。
示意图如下:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

快速排序算法实现

Linklist Quicksort(Linklist *listHead, Linklist *listTail)
{
	Lnode *current;
	Lnode* lessHead = NULL, *lessTail = NULL, *moreHead = NULL, *moreTail = NULL;
	current = (*listHead)->next;//每次取首节点为枢纽,current指向第二个节点用于遍历
	if ((*listHead)->next != NULL)//当链表节点数不为1时(说明链表未排好序)
	{
		for (current = (*listHead)->next; current; current = current->next)
		{
			if (current->key < (*listHead)->key)
			{
				if (lessHead == NULL)
					lessHead = current;
				else
					lessTail->next = current;
				lessTail = current;
			}//current结点key小于枢纽key时放入less链表
			else
			{
				if (moreHead == NULL)
					moreHead = current;
				else
					moreTail->next = current;
				moreTail = current;
			}//current结点key大于枢纽key时放入more链表
		}
		//根据枢纽结点将T链表分为less和more两个链表
		if (moreTail)
			moreTail->next = NULL;
		if (lessTail)
			lessTail->next = NULL;
		//将more链表尾结点next域置空
		if (moreHead != NULL)
		{
			moreTail->next = NULL;
			Quicksort(&moreHead, &moreTail);
			(*listHead)->next = moreHead;
			*listTail = moreTail;
		}
		//若moreHead不空,则current为more链表的尾结点,对more链表进行递归处理,将more链表接在枢纽节点后
		else
		{
			(*listHead)->next = NULL;
			*listTail = *listHead;
		}
		//若moreHead为空,则只有less链表(即结点key全小于枢纽),将枢纽结点接在less节点后
		if (lessHead != NULL)
		{
			lessTail->next = NULL;
			Quicksort(&lessHead, &lessTail);
			lessTail->next = *listHead;
			*listHead = lessHead;
		}
		//若lesseHead不空,对less链表进行递归处理,再将枢纽节点接在less链表后
		else
		{
			lessHead = *listHead;
		}
		//若lesseHead为空,则枢纽结点作为首节点
		return lessHead;
	}
	else
		return *listHead;
}

整个程序源代码

#include<stdio.h>
#include<malloc.h> 
typedef struct Lnode
{
	int key;
	struct Lnode* next;
}Lnode, *Linklist;
//链表结构体类型
Linklist createList(Linklist L, int n)
{
	L = (Linklist)malloc(sizeof(Lnode));
	L->next = NULL;
	Lnode *p, *r;
	r = L;
	p = (Lnode*)malloc(sizeof(Lnode));
	scanf("%d", &r->key);
	for (int i = 1; i < n; i++)
	{
		p = (Lnode*)malloc(sizeof(Lnode));
		scanf("%d", &p->key);
		r->next = p;
		r = p;
	}
	r->next = NULL;
	return L;
}
//初始初始化及尾插法(正序)创建单链表
Linklist getTail(Linklist L)
{
	while (L->next)
		L = L->next;
	return L;
}
//得到尾指针
void Print(Linklist L)
{
	Lnode *p;
	p = L;
	while (p)
	{
		printf("%d ", p->key);
		p = p->next;
	}
}
//遍历单链表
Linklist Quicksort(Linklist *listHead, Linklist *listTail)
{
	Lnode *current;
	Lnode* lessHead = NULL, *lessTail = NULL, *moreHead = NULL, *moreTail = NULL;
	current = (*listHead)->next;//每次取首节点为枢纽,current指向第二个节点用于遍历
	if ((*listHead)->next != NULL)//当链表节点数不为1时(说明链表未排好序)
	{
		for (current = (*listHead)->next; current; current = current->next)
		{
			if (current->key < (*listHead)->key)
			{
				if (lessHead == NULL)
					lessHead = current;
				else
					lessTail->next = current;
				lessTail = current;
			}//current结点key小于枢纽key时放入less链表
			else
			{
				if (moreHead == NULL)
					moreHead = current;
				else
					moreTail->next = current;
				moreTail = current;
			}//current结点key大于枢纽key时放入more链表
		}
		//根据枢纽结点将T链表分为less和more两个链表
		if (moreTail)
			moreTail->next = NULL;
		if (lessTail)
			lessTail->next = NULL;
		//将more链表尾结点next域置空
		if (moreHead != NULL)
		{
			moreTail->next = NULL;
			Quicksort(&moreHead, &moreTail);
			(*listHead)->next = moreHead;
			*listTail = moreTail;
		}
		//若moreHead不空,则current为more链表的尾结点,对more链表进行递归处理,将more链表接在枢纽节点后
		else
		{
			(*listHead)->next = NULL;
			*listTail = *listHead;
		}
		//若moreHead为空,则只有less链表(即结点key全小于枢纽),将枢纽结点接在less节点后
		if (lessHead != NULL)
		{
			lessTail->next = NULL;
			Quicksort(&lessHead, &lessTail);
			lessTail->next = *listHead;
			*listHead = lessHead;
		}
		//若lesseHead不空,对less链表进行递归处理,再将枢纽节点接在less链表后
		else
		{
			lessHead = *listHead;
		}
		//若lesseHead为空,则枢纽结点作为首节点
		return lessHead;
	}
	else
		return *listHead;
}
int main()
{
	Lnode* L = NULL;
	int n;
	printf("请输入元素个数\n");
	scanf("%d", &n);
	printf("请输入元素\n");
	L = createList(L, n);
	Lnode* listTail;
	listTail = getTail(L);
	Quicksort(&L, &listTail);
	printf("排序后元素序列为\n");
	Print(L);
	return 0;
}

整个程序已在Visual Studio 2017上运行通过

测试案例

(1)一般数据样例

在这里插入图片描述

(2)只有一个数据时

在这里插入图片描述

(2)有重复数据时

在这里插入图片描述

总结

到此这篇关于C语言实现单链表的快速排序算法的文章就介绍到这了,更多相关C语言快速排序算法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++ VTK实例之高斯随机数的生成

    C++ VTK实例之高斯随机数的生成

    这篇文章主要介绍了VTK的一个实例之高斯随机数的生成,本文演示了从一个平均数是0.0和标准偏差是2.2的高斯分布中随机生成3个随机数。感兴趣的同学可以学习一下
    2021-11-11
  • c++中string和vector的详细介绍

    c++中string和vector的详细介绍

    这篇文章主要介绍了c++中string和vector的详细介绍,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下
    2022-09-09
  • EasyX实现自由落体小球

    EasyX实现自由落体小球

    这篇文章主要为大家详细介绍了EasyX实现自由落体小球,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • C++  系统IO流介绍

    C++  系统IO流介绍

    这篇文章主要介绍了C++系统IO流,大部分人都是从输出"Hello World"开始的,本文会介绍C++中的IO细节,需要的朋友可以参考一下,希望对大家有所帮助
    2021-12-12
  • 详解C语言实现猜数字游戏

    详解C语言实现猜数字游戏

    这篇文章主要为大家介绍了C语言实现猜数字游戏,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助<BR>
    2022-01-01
  • C语言顺序表的实现代码

    C语言顺序表的实现代码

    这篇文章主要为大家详细介绍了C语言实现顺序表的实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09
  • C++实现动态绑定代码分享

    C++实现动态绑定代码分享

    对于C++动态绑定的理解,就是编译器用静态分析的方法加上虚拟函数的设计实现在程序运行时动态智能执行正确虚拟函数的技术。要彻底理解动态绑定,只需要掌握两点,一是编译器的静态编译过程,二是虚拟函数的基本知识。只要有了这两点理解,任何动态绑定的分析都是很容易的
    2015-11-11
  • C++ IO设备读写功能实现详解

    C++ IO设备读写功能实现详解

    C++的文件IO(Input,Output)操作就是指对文件进行读写(输入与输出)的操作。输入就是从磁盘上的文件中读取内容到内存中。输出就是将内存中的数据内容输出或者说写入到磁盘的文件中,这篇文章主要介绍了C++ IO设备读写功能实现
    2022-11-11
  • C++插入排序算法实例详解

    C++插入排序算法实例详解

    这篇文章主要为大家详细介绍了C++插入排序算法实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • C++语言中std::array的用法小结(神器用法)

    C++语言中std::array的用法小结(神器用法)

    这篇文章主要介绍了C++语言中std::array的用法小结,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11

最新评论