北邮2019计导下 链表一 B. 实验11_10_链表排序

单链表确实是不好操作,只能顺着毛摸的感觉很不爽。
提示是用选择排序、插入排序或冒泡排序,其实这仨时间复杂度都是O(n^2),选哪个对效率影响不大。
我所以选择冒泡排序,是因为选择排序和插入排序都涉及到不相邻节点的对换问题,从D题可以看出,不相邻节点的对换涉及到四个节点的指针域的更改,而且相邻时反而要特别考虑,比较麻烦,
而冒泡排序一趟在中只涉及到相邻两个节点的比较和更改,相对而言比较方便。

冒泡排序中可能涉及到的节点交换

灵魂画师上线
北邮2019计导下 链表一 B. 实验11_10_链表排序_第1张图片
画图想一想,只是多,不难
1、第一个节点和第二个节点
用prevPtr=NULL来标志第一个节点,此时!prevPtr为true
1)需要交换时:
isChanged标志位为1
头结点入口更改
a的节点域改成b的节点域
b的节点域改成a
2)不需要交换时
isChanged标志位仍然为0,啥也不干
根据isChanged标志位,指针迭代的方式也不同
更换过:b的节点域改成a的后继节点
标志位重置为0
没更换过:a和b各自向后平移一位
此时第一次的BUFF结束,prev被设置为头节点(头结点必须用*sPtr获取,因为头结点的位置可能会更改,用headPtr保存的话可能实际上变成了第二个节点)
2、一般情况
1)需要交换时:
isChanged设置为1
a的节点域改成b的节点域
prev的节点域改成b
b的节点域改成a
2)不需要交换时:
isChanged标志位仍然为0,啥也不干
根据isChanged标志位,指针迭代的方式也不同
更换过:isChanged重置为0
prev向后迭代
b成为a的后继
a成为prev的后继
没有更改:prev、a、b各自向后迭代

#include 
#include 
typedef struct _node
{
	int data;
	struct _node* nextPtr;
} Node;

Node* createList();
void destoryList(Node** sPtr);
int lenList(Node **sPtr);//用于计算链表内的节点数
void bubbleSortList(Node **sPtr);
void printList(Node **sPtr);

int main(int argc, char const *argv[])
{
	Node *headPtr=createList();
	bubbleSortList(&headPtr);
	printf("The new list is:");
	printList(&headPtr);
	destoryList(&headPtr);
	return 0;
}

Node* createList()
{
	Node *currentPtr,*headPtr=NULL,*lastPtr;
	int num;
	scanf("%d",&num);
	while(num!=-1)
	{	
		currentPtr=(Node*)malloc(sizeof(Node));
		currentPtr->data=num;
		if(!headPtr)
			headPtr=lastPtr=currentPtr;
		else
		{
			lastPtr->nextPtr=currentPtr;
			lastPtr=currentPtr;
		}
		scanf("%d",&num);
	}
	lastPtr->nextPtr=NULL;
	return headPtr;
}

void destoryList(Node **sPtr)
{
	Node *headPtr=*sPtr,*tmpPtr;
	while(headPtr)
	{
		tmpPtr=headPtr;
		headPtr=headPtr->nextPtr;
		free(tmpPtr);
	}
	*sPtr=NULL;
}

void printList(Node **sPtr)
{
	Node *headPtr=*sPtr;
	while(headPtr)
	{
		printf("%d%c",headPtr->data,(headPtr->nextPtr)?' ':'\n');
		//如果节点接下来还有节点就打印空格,否则意味着遍历结束,打印回车
		headPtr=headPtr->nextPtr;
	}
}

int lenList(Node **sPtr)//实际上是统计从头结点跳转到尾结点的次数
{
	Node *headPtr=*sPtr;
	int cnt=0;
	while(headPtr)
	{
		cnt++;
		headPtr=headPtr->nextPtr;
	}	
	return cnt;
}

void bubbleSortList(Node **sPtr)
{
	int n=lenList(sPtr),i,j,isChanged;
	//需要设置isChanged标志位,交换与否对之后的节点迭代有影响,要分开考虑
	if(n==0||n==1) return ;//空链表或者只有一个元素有什么好排序的
	Node *aPtr,*bPtr,*prevPtr;
	for(i=1;i<n;++i)//冒泡排序,共跑n-1趟,仅作为计数,没有啥意义
	{
		prevPtr=NULL;//每跑一趟,初始时前置节点设为NULL
		aPtr=(*sPtr);//a是链表头结点,因为在排序中可能会改变链表的入口,每次需要重新获取
		bPtr=aPtr->nextPtr;//b是第二个节点,冒泡排序是相邻两个排序嘛
		isChanged=0;//标志位,交换与不交换时,迭代的操作时不一样的
		for(j=0;j<n-i;++j)//冒泡每趟检查n-i-1次
		{
			if(!prevPtr)//没有前置节点,即为头结点时
			{
				if((aPtr->data)>(bPtr->data))//说明需要交换头结点和第二个节点
				{
					*sPtr=bPtr;//链表的入口改成b节点
					aPtr->nextPtr=bPtr->nextPtr;//a节点的后继变成b节点的后继
					bPtr->nextPtr=aPtr;//把a节点接到b后面
					isChanged=1;//标志位,说明发生了改变
				}
				if(isChanged)//若交换过前两个节点
				{
					bPtr=aPtr->nextPtr;//还是保持a前b后
					isChanged=0;
				}
				else//没有交换就各自向后迭代
				{
					bPtr=bPtr->nextPtr;
					aPtr=aPtr->nextPtr;
				}
				prevPtr=*sPtr;//prevPtr指向链表入口
			}
			else//不是头结点,即对中间的两个节点检查时
			{
				if((aPtr->data)>(bPtr->data))
				{
					isChanged=1;
					aPtr->nextPtr=bPtr->nextPtr;//a的后继改成b的后继
					prevPtr->nextPtr=bPtr;//把b接上前面节点
					bPtr->nextPtr=aPtr;//a节点接到b节点后面
				}
				if(isChanged)//交换过
				{
					isChanged=0;
					prevPtr=prevPtr->nextPtr;//前置节点向后迭代
					bPtr=aPtr->nextPtr;//保持a前b后
					aPtr=prevPtr->nextPtr;
				}
				else//三个指针各自向后迭代
				{
					bPtr=bPtr->nextPtr;
					aPtr=aPtr->nextPtr;
					prevPtr=prevPtr->nextPtr;
				}
			}
		}
	}
}

题目描述
问题描述:已知一个正整数组成的无序序列,个数未知,但至少有一个元素,你的任务是建立一个单链表,并使用该链表存储这个正整数序列,然后将这个链表进行排序,使得排序后的链表为递增序列。正整数的输入用-1作为结束标志,注意-1不算这个正整数序列中的元素(不要统计-1)。在排序的过程中,你可以自己选择排序算法(冒泡排序、选择排序等),但必须是通过修改结点的指针域来进行排序,而不是对结点的数据域进行修改。程序结束后要释放所有节点占据的空间。
输入与输出要求:输入一个元素个数未知的正整数序列,以输入“-1”结束,输入“-1”前至少输入一个正整数。输出经过排序后的链表,每个元素后有一个空格,注意最后一个元素后只有换行符。
数据最多的测试用例节点数在1000这个数量级,所有整数可以用int型存储。
请注意输入输出格式。

输入样例
49 38 65 97 76 13 27 49 -1
输出样例
The new list is:13 27 38 49 49 65 76 97

你可能感兴趣的:(Excited,OJ)