数据结构代码题---插曲<插入排序顺序+链式实现>

数据结构代码题之插入排序实现(顺序和链式)

文章主要针对直接插入排序和简单选择排序进行讲解,一般的教材书上都是以顺序表进行两种排序方式的实现,但是对于链式结构的实现介绍内容较少。原因之一便是,对于链表的操作排序会使得操作非常的复杂,同时时间复杂度与顺序表存储的还有不小的差距。

但对于考研数据结构题目来讲,实现链式的排序算法确显得尤为重要,毕竟学习要学明白,才可以在书写算法的时候游刃有余,以防在考试中能够心中有数,较为流畅的书写代码。

下面我们先对直接插入排序进行实现

直接插入排序

算法思路

  • 从第一个元素开始,该元素可以认为已经被排序。
这里要非常注意:
有的题目中问到对于当前的关键字序列给出第k趟的插入排序的时候
我们应该明白,插入排序的第一趟是对题目序列原封不动的序列,
正常的插入排序需要在第二趟进行操作。
  • 类似于我们日常生活中的打牌操作,在拿到一张牌的时候,我们要首先对手里的牌进行从后向前看,找到合适的位置,将牌插入手中的牌中,插入排序的方式于上述类似;
  • 对于待排序元素,将其与前方的元素进行依次比较,在找到一个元素比当前元素小时,其后面的元素的位置便是插入的位置;
  • 对于在比较的过程中,比当前元素数值大的元素,需要进行后移,
  • 移动完成后,将其合适的位置插入待排序元素;
  • 重复上述步骤
  • 插入排序结束!

下面是根据上述的步骤进行的动态图演示,便于更好的理解。
在这里插入图片描述
下面是相关的柱形图的演示。

在这里插入图片描述
算法实现之顺序表存储

针对上述的算法思路,我们对实现方式进行编码设计:

//直接插入排序实现
	static void  insertSort(int[] arr){
		int i,j,temp;//初始化操作

		//由于第一趟原封不动,因此从元素位置1开始进行排序
		for(i = 1;i<arr.length;i++){//进行趟数循环

     		if(arr[i] < arr[i-1]){//前面的元素比当前元素大

     			//存储进行排序的元素
     			temp = arr[i];
     			
				//j从i-1开始,因为i是待排序元素
     			//若前面的元素比当前元素大,进行后移操作
     			//j>=0 && arr[j] > temp;代表后退的条件,当前元素比排序元素大
     			for(j = i-1;   j>=0 && arr[j] > temp;  j--){
     				arr[j+1] = arr[j];//进行后移
     			}

     		}
     		//在一趟结束后,对于temp的位置便是在j+1的位置有一个空位置
     			arr[j+1] = temp;
		}		

	}

直接插入排序链式实现

  • 首先是链式序列的结构体代码进行复习:
typedef struct ListNode{
    int data;//数据域
    struct node *next;//指针域
}ListNode,*LinkList;
  • 时间复杂度分析

    对于链表的插入排序元素时只要更新相邻节点的指针即可,不需要像数组一样将插入位置后面的元素往后移动,因此插入操作的时间复杂度是 O(1),但是找到插入位置需要遍历链表中的节点,时间复杂度是 O(n),因此链表插入排序的总时间复杂度仍然是 O(n^2)。

实现思路

  • 首先是对链表进行判空操作,若为空直接返回即可;
  • 其次是我们要对链表进行引入节点,为什么要引入新节点?

答:因为在链表插入排序的过程中,难免要实现在头节点之前要插入元素,但是如何实现在头节点前面进行插入呢,所以要引入一个节点,指向头节点。

  • 引入一个哑点dump,使得dump->next指向head(链表的头节点)
  • 设置一个lastsorted指针,指向排序序列的最后一个元素,初始时last等于head。
  • 设置一个操作指针cur,指向待排序数据,初始时cur指向head->next;

大致初始化操作图片如下:

  • 其次是实现逻辑:相比于顺序存储,链式存储不需要进行元素的移动,只需要进行插入便可以,就是操作指针麻烦一些。
  • 比较lastsorted和cur指针的数值大小
    • 若lastsorted指向的数值 < cur指向的数值,这是正常的操作步骤,不需要进行插入的,因此只需要进行前进移动即可:
	lastSorted = lastSorted->next;
	cur = lastSorted->next;
  • 若lastsorted指向的数值 > cur指向的数值,则为不正常的步骤,需要新建一个插入cur位置的前驱节点pre,需要进行插入,实现代码如下:
	//按照从后往前进行添加的方式
	lastSorted->next = curr->next;
    curr->next = pre->next;
    //pre指向cur的前驱
    pre->next = curr;

代码或许难以理解如何操作的,下面看图便可:
在这里插入图片描述

  • 重复上述步骤,直到cur指向null便是结束排序结束
  • 返回dump->next为结果链表

结合上述的操作逻辑进行编码如下:

//插入排序(链式实现)
 	void insertSortLinkList(LinkList * head){
 		//进行判空操作
 		if(head == NULL){
 			return head;
 		}

 		//并不为空操作
 		//设置一个dump指向头节点
 		ListNode *dump = (ListNode*)malloc(sizeof(ListNode));
 		dump->next = head;//初始化节点
 		ListNode *lastSort = head;
 		ListNode * cur = head->next;//初始化操作指针

 		//进行操作排序
 		while(cur != NULL){
 			//进行lastSort与cur数值比较
 			if(lastSort->data <= cur->data){
 				lastSort=lastSort->next;//进行前进
 			}else{
 				//若大于
 				//开辟一个插入位置前序节点指针
 				ListNode* pre = dump;
 				//进行pre的位置调节,找到待插入位置的前一个位置
 				while(pre->next->data  <= cur->data){
 					pre = pre->next;
 				}

 				//进行插入
 				lastSort->next = cur->next;
 				cur->next = pre->next;
 				pre->next = cur;

 			}

 			//重置cur的位置
 			cur = lastSort->next;
 		}

 		//返回链表
 		return dump->next;//这里dump->next就代表head

 	}

代码整合

typedef struct ListNode{
    int data;//数据域
    struct node *next;//指针域
}ListNode,*LinkList;

//插入排序(链式实现)
 	void insertSortLinkList(LinkList * head){
 		//进行判空操作
 		if(head == NULL){
 			return head;
 		}

 		//并不为空操作
 		//设置一个dump指向头节点
 		ListNode *dump = (ListNode*)malloc(sizeof(ListNode));
 		dump->next = head;//初始化节点
 		ListNode *lastSort = head;
 		ListNode * cur = head->next;//初始化操作指针

 		//进行操作排序
 		while(cur != NULL){
 			//进行lastSort与cur数值比较
 			if(lastSort->data <= cur->data){
 				lastSort=lastSort->next;//进行前进
 			}else{
 				//若大于
 				//开辟一个插入位置前序节点指针
 				ListNode* pre = dump;
 				//进行pre的位置调节,找到待插入位置的前一个位置
 				while(pre->next->data  <= cur->data){
 					pre = pre->next;
 				}

 				//进行插入
 				lastSort->next = cur->next;
 				cur->next = pre->next;
 				pre->next = cur;

 			}
 			
 			//重置cur的位置
 			cur = lastSort->next;
 		}

 		//返回链表
 		return dump->next;//这里dump->next就代表head

 	}

上述便是对直接插入排序的顺序和链式存储操作讲解。

在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值