单链表的两种排序(冒泡和插入)优化

单链表冒泡排序和插入排序及其优化(多角度理解)

均以从小到大的顺序为例!

一:冒泡排序法

冒泡排序要点:1. 单链表的遍历 2. 相邻两个节点的交换(情况多样)

  1. 首先,肯定是两层循环的嵌套,比方说最外层是这样:for(int i = 0; i < LengthList(head)-1; i++) 内层循环就比较多样了,这里就不举其他例子了,直接给出我写的一种(先等我说完):

  2. 相邻节点的交换需要考虑(正常情况下):(1). 节点数为2,头尾节点的交换 (2). 节点数为3以上包括3,头节点和相邻节点的交换 (3). 节点数为3以上包括3,相邻节点与尾节点的交换 (4). 节点数为4以上包括4,中间节点2,3的交换

    你肯定会想:在(2).中情况(1).和(2).不是一样的吗,都是头节点与相邻节点的交换?包括(3).和(4).不是一样的吗?诸如此类,其实冒泡排序就是这样,其实交换节点只要用一种方法就行了,但是,你在计算机内部排好序总有输出(要不然看不到),这样就出大麻烦了,因为链表头部在动态变化着,也许排序完成后,头变成了第三个节点,这样从头打印直接会造成节点的丢失。

    一句话:交换节点考虑节点位置不同带来的诸多情况,是为了及时更新链表的头尾,说白了,就是讲,哪个节点与哪个节点交换会影响到表头或者表尾,而另外一些交换不会

    废话说完了,关门放代码:

    注:这里的表头head和表尾end均为全局变量

   void BubbleSort(void)
   {
   	   Node * pre, * p, * tail = NULL;
   	   pre = p = head;
   	   while (p->next != tail)
   	   {
   		   p = head->next;
   		   while (p != tail)
   		   {
   			   if (pre->num > p->num)
   			   { 
   			        SwapNode(pre, p);
   			        Node * temp = pre;
   				    pre = p;
   				    p = temp;
   			   } 
   			   pre = pre->next;
   			   p = p->next;
   		   }
   		   tail = pre;
   		   pre = p = head;
   	  }
      PrintList(head);return;
   }

源代码会再最后给出,这里仅仅拿出部分函数分析

所以,想要改进目标已经很明确了

那就是,在原来的头节点前插入一个新的节点(数据域不处理),使它与head相连

冒泡排序时,第一个数据实际上时第二个节点的数据,换句话说,新的头是个空头,它只是一个定位的作用,无论后面的节点怎么交换,这个空头节点的下一个节点一定可以作为头节点打印出整个链表的所以节点的数据

所以初步改进后代码如下:

   void Bubble_Sort(void)
   {
        AddFalseHead();
   	    Node * p, * q, * tail = NULL;while (head->next->next != tail){
   ​		    p = head->next;
   ​		    q = p->next;while (q != tail){if (p->num > q->num){
   ​				   Node * pre = FindPreNode(p);
   ​				   pre->next = q;
   ​				   p->next = q->next;
   ​			  	   q->next = p;
   ​				   Node * temp = p;
   ​				   p = q;
   ​				   q = temp;}
   			   p = p->next;
   			   q = q->next;
   	        }
   		    tail = p;
   	}
   	while (end->next != NULL)
   	   end = end->next;
   	head = head->next;
   	PrintList(head);
   	
   	return;
}

最后改进 if(){}里面函数调用问题,尝试不用FindPreNode()函数进行节点交换

   void Bubble_Sort(void)
   {
   	   AddFalseHead();
   	   Node * p, * q, * tail = NULL;
     //神龙顾首不顾尾,头head的问题解决了,又有尾部问题
     //随之而来的问题是end没有及时更新,需要手动寻找(滑稽)
     //函数末尾用局部遍历寻找尾部 
      while (head->next->next != tail)
      {
      	  p = head;
      	  q = head->next;
      	  while (q->next != tail)
      	  {
      		  if (q->num > q->next->num)
      		  {
      			 p->next = q->next;
      			 Node * temp = q->next->next;
      		     q->next->next = q;
      			 q->next = temp;
      			 q = p->next;
   			  }
   			  p = p->next;
   			  q = q->next;
   		  }
      	  tail = q;
   	}
   //精简了上面 if(){}中节点的交换(至少不需要再调用函数) 
   	while (end->next != NULL)
   	   end = end->next;
   	head = head->next;
   	PrintList(head);
   
   	return;
}

源代码:

#include <stdio.h>
#include <stdlib.h>
#define N 10 

typedef struct Node
{
	int num;
    struct Node * next;
}Node;

Node * head = NULL;
Node * end = NULL;

void CreatList(void);
void BubbleSort(void);
void Bubble_Sort(void);
void PrintList(Node * head);
int LengthList(Node * temp);
void InsertListEnd(Node * p);
void SwapNode(Node * p, Node * q);
Node * FindList(Node * p);
void AddFalseHead(void);
Node * FindPreNode(struct Node * p);

int main(void)
{
	int len; 
	
	CreatList();
	len = LengthList(end);
	printf("len = %d\n", len);
	
//	InsertListEnd(end);
/*	len = LengthList(head);
	printf("len = %d\n", len);
*/	
	Bubble_Sort();
	
	return 0;
}

void CreatList(void)
{
	int n = N;
	
	while (n --)
	{
		Node * temp = (Node *)malloc(sizeof(Node));
		if (temp == NULL)
		{
			printf("Mallloc error!\n");
			exit(0);
		}
		else
		{
			printf("Plz input a number:\n");
			scanf("%d", &temp->num);
			temp->next = NULL;
			if (head == NULL)
			{
				head = temp;
				end = temp;
			}
			else 
			{
				end->next = temp;
				end = temp;
			}
		}
	}
	PrintList(head);
	
	return;
}

void PrintList(Node * head)
{
    if(head == NULL)
    {
        printf("Empty List!\n");
    }
    while (head)
    {
    	 if(head)
	       printf("->");
       printf("%d ", head->num);
       head = head->next;
    }
    printf("\n");
}

void InsertListEnd(Node * p)
{
	if (head == NULL)
	{
		printf("Empty List!\n");
		return;
	}
	else
	{
		Node * pt = FindList(p);
	    Node * temp = (Node *)malloc(sizeof(Node));
	   
		printf("Plz input a number:\n");
		scanf("%d", &temp->num);
		if (pt == end)
		{
			pt->next = temp;
			end = temp;
			end->next = NULL;
   	}
   	else
   	{
   		temp->next = pt->next;
   		pt->next = temp;
   		
		}
		printf("InsertListENd's mission is over!\n");
		PrintList(head);
	}
	
	return;
}

int LengthList(Node * temp)
{
	int len = 1;
	Node * pt = head;
	while (pt != temp)
	{
		len++;
		pt = pt->next;
	}

	return len;
}

void SwapNode(Node * p, Node * q)
{
	if (p == q || p == NULL || q == NULL)
	{
		printf("Ineffective swap!\n");
		return;
	}
	
	if(LengthList(p) > LengthList(q))
	{
		Node * temp = p;
		p = q;
		q = temp;
	}
	
	if (p == head)
	{
		if (p->next == q && q == end)
		{
		   end->next = p;
		   p->next = NULL;
		   head = end;
		   end = head->next;	
		}
	
	   if (p->next == q && q != end)
		{
			p->next = q->next;
			q->next = p;
			head = q;
		}
		
		else if (p->next != q && q == end)
		{
			Node * temp = FindPreNode(q);
			temp->next = p;
			q->next = p->next;
			p->next = NULL;
			end = p;
			head = q;
		}
		
		else if (p->next != q && q != end)
		{
			Node * temp = FindPreNode(q);
			temp->next = p;
			p = p->next;
			head->next = q->next;
			q->next = p;
			head = q;
		}
		
		else   
		   printf("1.Sorry! Not take into account this case!\n");
	}
	else
	{
		if (p->next == end && q == end)
		{
			Node * temp = FindPreNode(p);
			temp->next = q;
			q->next = p;
			p->next = NULL;
			end = p;
		}
		
		else if (p->next != end && q == end)
		{
			Node * temp1 = FindPreNode(p);
			Node * temp2 = FindPreNode(q);
			temp1->next = q;
			q->next = p->next;
			temp2->next = p;
			p->next = NULL;
			end = p;
		}
		
		else if (p->next == q && q != end)
		{
			Node * temp = FindPreNode(p);
			temp->next = q;
			p->next = q->next;
			q->next = p;
		}
		
		else if (p->next != q && q != end)
		{
			Node * temp1 = FindPreNode(p);
			Node * temp2 = FindPreNode(q);
			Node * pt = p->next;
			temp1->next = q;
			p->next = q->next;
			q->next = pt;
			temp2->next = p;
		}
		
		else
		   printf("2.Sorry! Not take into account this case!\n");
	}
	
//	PrintList(head);
	
	return;
}

Node * FindPreNode(struct Node * p)
{
	struct Node * temp = head;
	if (head == NULL)
	{
		printf("Empty list!\n");
		return NULL;
	}
	while (temp->next != p)
		temp = temp->next;
	
	return temp; 
}

Node * FindList(Node * p)
{
	struct Node * temp = head;
	if (head == NULL)
	{
		printf("Empty list!\n");
		return NULL;
	}
	else
	{
		while (temp)
		{
			if (temp == p)
			{
			   return temp;
			}
			temp = temp->next;
		}
	
		return NULL;
	}
}

void AddFalseHead(void)
{
	Node * phead= (Node *)malloc(sizeof(Node));
	phead->next = head;
	head = phead;
	printf("AddFalseHead's mission is over!\n");
   
	return ;
}

void BubbleSort(void)
{
	Node * pre, * p, * tail = NULL;
	pre = p = head;
	while (p->next != tail)
	{
		p = head->next;
		while (p != tail)
		{
			if (pre->num > p->num)
			{ 
			   SwapNode(pre, p);
			   Node * temp = pre;
				pre = p;
				p = temp;
			} 
			pre = pre->next;
			p = p->next;
		}
		tail = pre;
		pre = p = head;
	}
	PrintList(head);
	
	return;
}

void Bubble_Sort(void)
{
	Node * p, * q, * tail = NULL;
	AddFalseHead();
//神龙顾首不顾尾,头head的问题解决了,又有尾部问题
//随之而来的问题是end没有及时更新,需要手动寻找(滑稽)
//函数末尾用局部遍历寻找尾部 
/*	方法一:
    while (head->next->next != tail)
	{
		p = head->next;
		q = p->next;
		while (q != tail)
		{
			if (p->num > q->num)
			{
				Node * pre = FindPreNode(p);
				pre->next = q;
				p->next = q->next;
				q->next = p;
				Node * temp = p;
				p = q;
				q = temp;
			}
			
			p = p->next;
			q = q->next;
      }

		tail = p;
	}
*/
// 方法二:
   while (head->next->next != tail)
   {
   	p = head;
   	q = head->next;
   	while (q->next != tail)
   	{
   		if (q->num > q->next->num)
   		{
   			p->next = q->next;
   			Node * temp = q->next->next;
   		   q->next->next = q;
   			q->next = temp;
   			q = p->next;
			}
			p = p->next;
			q = q->next;
		}
     	tail = q;
	}
//	精简了上面 if(){}中节点的交换(至少不需要再调用函数) 
	while (end->next != NULL)
	   end = end->next;
	head = head->next;
	PrintList(head);
	
	return;
}

注:源码包含三个冒泡排序:BubbleSort(), Bubble_Sort()方法一,Bubble_Sort()方法二,分别对应上述我讲的三种思路


二:插入排序法

插入排序的要点(思路):1. 插入位置的寻找顺序 2. 对插入节点的处理

  1. 建议按照单链表的顺序从头寻找,找到位置就在该位置前插入

  2. 需要执行插入的节点有两种处理方式,

    一:"复刻"此节点,插入位置后,删除该节点,比如1->3->5->4->2,现将4插入5前面,先(第一 步)插 1->3->4->5->4->2,后(第二步)删 1->3->4->5->2

    二:挖出该节点,再插入位置,1->3->5->4->2, 1->3->5->2,1->3->4->5->2

    好了,准备完成,依旧是安装"假表头",用AddFalseHead()建一个空头,执行插入

    处理方式一代码:

void InsertSort(void)
{
	AddFalseHead();
	Node * pst = head->next;
	Node * q = head->next->next;
	
	while (q != NULL)
	{
		Node * qpre = q;
		while (pst != q)
		{
			if (pst->num > q->num)
			{
				Node * temp = q->next;
			    InsertListHead(pst, q);
			    q = temp; break;
			}
			pst = pst->next;
		}
		if (qpre == q)   q = q->next;
		pst = head->next;
	}
	head = head->next;
	PrintList(head);
	
	return;
}

处理方式二代码:

void InsertSort(void)
{
	AddFalseHead();
    Node * p = head->next;//第一节点 
    Node * q = p->next;
   
	while (q != NULL)
	{
		Node * pst = head->next;
		while (pst != q)
		{
			if (pst->num > q->num)
			{
				Node * temp = FindPreNode(pst);
				p->next = q->next;
				temp->next = q;
				q->next = pst;
				q = p;  break;
			}
			pst = pst->next;
		}
		if (p != q)
		  p = p->next;
		q = q->next;
	}	
	head = head->next;
	PrintList(head);
	
	return;
}   

源代码:

#include <stdio.h>
#include <stdlib.h>
#define N 10

typedef struct Node
{
	int num;
   struct Node * next;
}Node;

Node * head = NULL;
Node * end = NULL;

void CreatList(void);
void InsertSort(void);
void AddFalseHead(void);//"安(装)假头",即在头节点前再置新节点作为假头 
void DeletList(Node * movp);//称其为假头是因为其数据域为空(无效数据),便于插入 
void PrintList(Node * head);
Node * FindPreNode(Node * p);
void InsertListHead(Node * temp, Node * movp);

int main(void)
{
	CreatList();
	PrintList(head);
	InsertSort();
	
	return 0;
} 

void CreatList(void)
{
	int n = N;
	
	while (n --)
	{
		Node * pt = (Node *)malloc(sizeof(Node));
		printf("Plz input a number:\n");
		scanf("%d", &pt->num);
		if (head == NULL)
		{
			head = pt;
			end = head;
		}
		else
		{
			end->next = pt;
			end = pt;
			end->next = NULL;
		}
	}
	
	return;
}

void InsertListHead(Node * temp, Node * movp)
{
	Node * pre = FindPreNode(temp);
	Node * pt = (Node *)malloc(sizeof(Node));
	pt->num = movp->num;
	pre->next = pt;
	pt->next = temp;
	
	DeletList(movp);
	PrintList(head->next);
	
	return;
}

void DeletList(Node * movp)
{
	struct Node * pt = head;
	
	while (pt->next != movp)  pt = pt->next;
	if (end == movp)
	{
	   end = pt;
	   end->next = NULL;
	}
	else
	   pt->next = movp->next;
	free(movp);
}

Node * FindPreNode(struct Node * p)
{
	struct Node * temp = head;
	
	while (temp->next != p) 
	   temp = temp->next;
	   
	return temp; 
}

void PrintList(Node * head)
{
    if(head == NULL)
    {
        printf("Empty List!\n");
    }
    while (head)
    {
    	 if(head)
	       printf("->");
       printf("%d ", head->num);
       head = head->next;
    }
    printf("\n");
}

void AddFalseHead(void)
{
	Node * pt = (Node *)malloc(sizeof(Node));
	pt->next = head;
	head = pt;
} 

void InsertSort(void)
{
	AddFalseHead();
/*	方法一:
    Node * pst = head->next;
	Node * q = head->next->next;
	
	while (q != NULL)
	{
		Node * qpre = q;
		while (pst != q)
		{
			if (pst->num > q->num)
			{
				Node * temp = q->next;
			   InsertListHead(pst, q);
			   q = temp; break;
			}
			pst = pst->next;
		}
		if (qpre == q)   q = q->next;
		pst = head->next;
	}
	head = head->next;
	PrintList(head);

*/ 
// 方法二:
   Node * p = head->next;//第一节点 
   Node * q = p->next;
   
	while (q != NULL)
	{
		Node * pst = head->next;
		while (pst != q)
		{
			if (pst->num > q->num)
			{
				Node * temp = FindPreNode(pst);
				p->next = q->next;
				temp->next = q;
				q->next = pst;
				q = p;  break;
			}
			pst = pst->next;
		}
		if (p != q)
		  p = p->next;
		q = q->next;
	}	
	head = head->next;
	PrintList(head);
	
	return;
}

注:源码中包含两个插入排序,InsertSort()方法一,InsertSort()方法二,分别对应上述我讲的两种思路
所有函数在源代码中均可找到,两个源代码中函数可能同名,内容不同作用不同,如有兴趣可以直接下载源码进行调试,拜拜啦

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值