非循环单链表的选择与插入排序
1.简介
排序是学习数据结构与算法中最有趣的知识之一,近期学习链表时,重点研究了选择排序和插入排序,经过了反复的推敲,最后明白
了一些,但又发现思维不够清晰,故打算写这篇博文,其重点是用来理清自己的思维,从而掌握这种算法。
2.算法的核心思想---有规律的改变链表中各节点间的连接方式。
核心就是规律(记得电视剧《天道》里有句话:按规律办事的人就是神~~~),可见规律的重要性,而在我的认识中:自然的规律就
是科学,人的规律就是哲学…扯远了。
选择排序的规律:遍历整个原链表,找到<数据>最小(从小到大排序)或最大(从大到小排序)的节点。
在链表的结构中把它删除,但内存中一定要保留。
将其追加到新的一个链表的末端。
~原链表会越来越短,而新链表会越来越长。~
最终原链表为空,而新链表会以<数据>从小到大或从大到小依次排列。
插入排序的规律:将原链表的第一个节点从其结构中删除,原链表结构从第二个节点开始。
将删除的节点作为目前新链表的唯一节点。
遍历原链表,依次取出各节点。
将取出的节点依据<数据>的大小有序的插入新链表。
~原链表会越来越短,而新链表会越来越长。~
最终原链表为空,新链表会以<数据>从小到大或从大到小依次排列。
3.选择排序的伪算法
定义原链表第一个节点的指针与新链表第一个节点的指针
while( 原链表不为空 ){
1.在原链表中找到最小或最大的数据节点。
2.在原链表结构中删除此最小节点。
3.将删除的最小节点追加到新链表中。
}
4.插入排序的伪算法
定义原链表第一个节点的指针与新链表第一个节点的指针;
新链表指针指向原链表的第一个节点。
原链表指针指向原链表的第二个节点。
while( 原链表不为空 ){
1.在原链表结构中有序的删除一个节点。
2.遍历新链表,找到刚好比删除节点<数据>大的那个节点。(从小到大的排序)
3.将删除节点插入到新链表中那个刚好比它大的节点前。
}
5.代码实现
下面的代码我混合实现了有头节点链表与无头节点链表的选择排序与插入排序。这里不得不说下我的排序函数原型:--以选择排序为例。
PNODE SelectSortList( PNODE List, char type, char *head, int (*cmp)(PNODE p1, PNODE p2 ) );
PNODE List:是链表的指针
char type:是排序方式 '<'代表的是从小到大排序,'>'代表是从大到小排序,不接受其它字符
char *head:是一个字符串指针,该字符串为HEAD时说明是有头节点链表,结果可以用返回值,也可以用List参数。为其它字符串时,说明是无头节点链表,结果必须用返回值,该参
数不接受NULL指针
int(*cmp )( PNODE p1, PNODE p2 )是回调函数,用于参数比较。
Return:返回的是排序后的链表,对于无头节点链表调用该函数必须接收这个返回值。
测试主函数
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define TRUE 1
# define FLASE 0
# define MALLOC( type, len ) (type*)malloc( sizeof(type) * len )
# include "sort.c"
int
main( void ){
PNODE List = NULL;
PNODE pHead = NULL;
printf( "************************无头节点链表排序测试****************************\n");
printf( "追加函数测试: ");
List = AppendList( List, 5 );
AppendList( List, 11 );
AppendList( List, 8 );
AppendList( List, -3 );
AppendList( List, 0 );
AppendList( List, 8 );
AppendList( List, 24 );
AppendList( List, 11 );
AppendList( List, 7 );
TraversList( List,"NOHEAD", Print );
printf( "\n选择排序函数由小到大测试: " );
List = SelectSortList( List, '<', "NOHEAD", CompareNum );
TraversList( List, "NOHEAD", Print );
printf( "\n选择排序函数由大到小测试: " );
List = SelectSortList( List, '>', "NOHEAD", CompareNum );
TraversList( List, "NOHEAD", Print );
printf( "\n插入排序函数由小到大测试: " );
List = InsertSortList( List, '<', "NOHEAD", CompareNum );
TraversList( List,"NOHEAD", Print );
printf( "\n插入排序函数由大到小测试: " );
List = InsertSortList( List, '>', "NOHEAD", CompareNum );
TraversList( List,"NOHEAD", Print );
DestoryList( List );
List = NULL;
printf( "\n***********************无头节点链表排序测试完毕*************************\n\n\n");
printf( "*************************有头节点链表排序测试***************************\n");
printf( "追加函数测试: ");
pHead = CreateHeadNode();
AppendList( pHead, 8 );
AppendList( pHead, 113 );
AppendList( pHead, 52 );
AppendList( pHead, -33 );
AppendList( pHead, 10 );
AppendList( pHead, 34 );
AppendList( pHead, 52 );
AppendList( pHead, -18 );
AppendList( pHead, -33 );
TraversList( pHead,"HEAD", Print );
printf( "\n选择排序函数由小到大测试: " );
SelectSortList( pHead, '<', "HEAD", CompareNum );
TraversList( pHead, "HEAD", Print );
printf( "\n选择排序函数由大到小测试: " );
SelectSortList( pHead, '>', "HEAD", CompareNum );
TraversList( pHead, "HEAD", Print );
printf( "\n插入排序函数由小到大测试: " );
InsertSortList( pHead, '<', "HEAD", CompareNum );
TraversList( pHead, "HEAD", Print );
printf( "\n插入排序函数由大到小测试: " );
InsertSortList( pHead, '>', "HEAD", CompareNum );
TraversList( pHead,"HEAD", Print );
DestoryList( pHead );
printf( "\n************************有头节点链表排序测试完毕************************\n\n");
return EXIT_SUCCESS;
}
链表排序函数库
/*************************************************************
* 非循环单链表选择与插入排序演练 *
************************声明*********************************/
/* 数据结构 */
typedef struct node{
long num; //编号,用于排序
struct node *pNext;//下一个节点
} NODE,*PNODE;
/* 接口函数 */
PNODE CreateHeadNode(); //创建一个头节点
PNODE AppendList( PNODE List, long New );//追加一个节点
PNODE SelectSortList( PNODE List, char type, char *head, int (*cmp)( PNODE p1, PNODE p2 ) );//选择排序
PNODE InsertSortList( PNODE List, char type, char *head, int (*cmp)( PNODE p1, PNODE p2 ) );//插入排序
void TraversList( PNODE List, char *head, void (*func)( PNODE p ) );//遍历链表
void DestoryList( PNODE List );//销毁链表
/* 操作函数 */
int CompareNum( PNODE p1, PNODE p2 );// 节点中 num数据的比较
void Print( PNODE p );//stdout 打印
/************************END**********************************/
/**********************接口函数*******************************/
/*
* Function: 创建一个头节点
* Return: PNODE指针
* */
PNODE CreateHeadNode(){
PNODE pHead;
pHead = MALLOC( NODE, 1 );
//分配一个头节点, 初始化 num = -100000 pNext = NULL
if( pHead != NULL )
pHead->num = -100000;
pHead->pNext = NULL;
return pHead;//返回头节点
}
/*
* Function: 追加一个节点
* List: 原链表
* long: 追加编号
* Return: 新链表 追加成功,NULL 追加失败
* */
PNODE AppendList( PNODE List, long num ){
PNODE new;
//分配空间
new = MALLOC( NODE, 1 );
if( new != NULL){
//写入数据
new->num = num;
new->pNext = NULL;
if( List != NULL ){
//找到末尾
while( List->pNext != NULL )
List = List->pNext;
//追加节点
List->pNext = new;
}else//对于无头链表的第一个节点写入
List = new;
return List;//无头链表的首次追加需要此返回值
}
return NULL;
}
/*
* Function: 选择排序
* List: 链表
* type: 模式,'<' 从小到大, '>' 从大到小
* head: 是否有头节点 "HEAD" 是有头节点, 其它字符串为无头节点,该参数不能为NULL
* cmp: 回调函数,用于比较方法,返回 1 0 -1
* Return: PNODE指针,指向排序后的链表
* */
PNODE SelectSortList( PNODE List, char type, char *head, int (*cmp)( PNODE p1, PNODE p2 ) ){
if( strcmp( head, "HEAD" ) == 0 ){//如果有头节点
//空链表参加排序,直接返回
if( List->pNext == NULL )
return List;
//定义变量并初始化
PNODE subList = List->pNext;//子链表指向首节点
PNODE tailNew; //新链表的尾指针
PNODE tmp, preTmp; //最小(大)节点指针,最小(大)节点前驱节点指针
PNODE p, pre; //遍历指针,遍历前驱节点指针
List->pNext = NULL; //新链表依旧用List这个头节点
tailNew = List;
//循环查找最小节点
while( subList != NULL ){
//参数初始化
tmp = subList;//当前最小节点
pre = subList;//当前遍历节点的前驱节点初始化
p = subList->pNext;//当前遍历节点初始化
preTmp = NULL;//当前最小节点前驱节点初始化
//循环比较
while( p != NULL ){
/* 由小到大排序时,当tmp > p 时,说明P是当前最小值,所以tmp = p
* 由大到小排序时,当tmp < p 时,说明p是当前最大值,所以tmp = p
* */
if( ( type == '<' && cmp( tmp, p ) == -1 ) || ( type == '>' && cmp( tmp, p ) == 1 ) )
tmp = p, preTmp = pre;
pre = p;//遍历
p = p ->pNext;
}
//在旧链表中删除最小节点
if( tmp == subList )//最小节点为首节点
subList = subList->pNext;
else//最小节点不为首节点
preTmp->pNext = tmp->pNext;
//追加最小节点至新链表
tmp->pNext = NULL;//尾结点pNext=NULL
tailNew->pNext = tmp;//尾结点指针遍历
tailNew = tailNew->pNext;
}
return List;
}else{//如果没有头节点
//空链表参加排序,直接返回
if( List == NULL )
return List;
//定义变量
PNODE new = NULL, tailNew;//新链表指针, 新链表尾指针
PNODE tmp,preTmp; //最小(大)节点指针,最小(大)节点前驱节点指针
PNODE p, pre; //遍历指针,遍历前驱节点指针
//循环拆分链表
while( List != NULL ){
//初始化参数
p = List->pNext;
pre = List;
tmp = List;
preTmp = NULL;
//循环查找最小节点
while( p != NULL ){
/* 由小到大排序时,当tmp > p 时,说明P是当前最小值,所以tmp = p
* 由大到小排序时,当tmp < p 时,说明p是当前最大值,所以tmp = p
* */
if( (type == '<' && cmp( tmp, p ) == -1) || ( type == '>' && cmp( tmp, p ) == 1 ) )
tmp = p, preTmp = pre;//获得当前最小节点指针与最小节点前驱节点指针
pre = p;//遍历指针与其前驱节点指针移动
p = p->pNext;
}
//在旧链表结构中删除最小节点
if( tmp == List )//最小节点为首节点时
List = List->pNext;
else
preTmp->pNext = tmp->pNext;//最小节点不为首节点时
//在新链表结构中追加最小节点
tmp->pNext = NULL;//追加的就是尾结点
if( new == NULL ){//追加第一个节点
new = tmp;
tailNew = new;//有了首节点才能初始化尾节点
}
else{//链表有首节点后的追加
tailNew->pNext = tmp;
tailNew = tailNew->pNext;
}
}
return new;//返回新链表
}
return NULL;
}
/*
* Function: 插入排序
* List: 链表
* type: 模式,'<' 从小到大, '>' 从大到小
* head: 是否有头节点
* cmp: 回调函数,比较方法
* Return: PNODE指针,指向排序后的链表
* */
PNODE InsertSortList( PNODE List, char type, char *head, int (*cmp)( PNODE p1, PNODE p2 ) ){
if( strcmp( head, "HEAD" ) == 0 ){
//空链表参加排序,直接返回
if( List->pNext == NULL )
return List;
//定义变量
PNODE subList;//子链表
PNODE tmp;//当前节点
PNODE pre, p;//新链表遍历指针
//参数初始化
subList = List->pNext->pNext;//子链表指向第二个有效节点
List->pNext->pNext = NULL;//头节点与第一个有效节点组成一个新链表
//循环插入节点
while( subList != NULL ){
//参数初始化
tmp = subList;//获取待插节点
p = List->pNext;//准备遍历指针
pre = List;//p 的前驱就是头节点
//寻找插入点
while( p != NULL ){
/* 由小到大排序时,当tmp < p 时,说明tmp要插入在p的前面
* 由大到小排序时,当tmp > p 时,说明tmp要插入在p的前面
* */
if( ( type == '<' && cmp( tmp, p ) == 1 ) || ( type == '>' && cmp(tmp, p ) == -1 ) )
break;
pre = p;//遍历
p = p->pNext;
}
//旧链表指向下一个待插参数
subList = subList->pNext;
//插入当前参数
pre->pNext = tmp;
tmp->pNext = p;
}
return List;
}else{
//空链表参加排序,直接返回
if( List == NULL )
return List;
//定义变量
PNODE new;//新链表
PNODE tmp;//当前待插节点
PNODE p, pre;//新链表遍历指针
//初始化参数
new = List;
List = List->pNext;//旧链表从第二个有效节点开始
new->pNext = NULL;//新链表保留旧链表第一个有效节点
//循环将旧链表数据依次插入新链表
while( List != NULL ){
//获得插入节点,初始化遍历新链表参数
tmp = List;
p = new;
pre = NULL;
//在新链表中循环找到链表的插入点
while( p != NULL ){
/* 由小到大排序时,当tmp < p 时,说明tmp要插入在p的前面
* 由大到小排序时,当tmp > p 时,说明tmp要插入在p的前面
* */
if( ( type == '<' && cmp( tmp, p ) == 1 ) || ( type == '>' && cmp( tmp, p ) == -1 ) )
break;
pre = p;
p = p->pNext;
}
//旧链表移向下一个待插入节点
List = List->pNext;
//在新链表中插入当前节点
if( p == new )//插入到首节点前
new = tmp;
else//插入到首节点后,任何节点前
pre->pNext= tmp;
tmp->pNext = p;
}
return new;
}
return NULL;
}
/*
* Function: 遍历链表
* List: 链表
* head: 是否含头结点 HEAD 是有,其它是无,不能用NULL
* func: 回调操作函数
* Return: void
* */
void TraversList( PNODE List, char *head, void (*func)( PNODE p ) ){
if( strcmp( head, "HEAD" ) == 0 ){
//有头节点
while( List->pNext != NULL ){
func( List->pNext );
List = List->pNext;
}
}else{
//无头节点
while( List != NULL ){
func( List );
List = List->pNext;
}
}
}
/*
* Function: 销毁链表
* List: 链表
* Return: void
* */
void DestoryList( PNODE List ){
PNODE t;
while( List != NULL ){
t = List;
List = List->pNext;
free( t );
t = NULL;
}
}
/************************END**********************************/
/**********************操作函数*******************************/
/*
* Function: 比较函数
* p1: 节点1
* p2: 节点2
* Return: p1 < p2 时返回 1, p1 > p2 时返回 -1
* */
int CompareNum( PNODE p1, PNODE p2 ){
if( p1->num < p2->num )
return 1;
else if( p1->num == p2->num )
return 0;
return -1;
}
/*
* Function: 标准输出打印
* p: 节点
* Return: void
* */
void Print( PNODE p ){
printf( " %3ld ", p->num );
}
/************************END*******************************/
图示
Writer: Anden Email: andensemail@163.com Time: 2016.04.5