直接插入排序算法

插入排序可以基于数组、也可以基于链表实现。

基于数组的优化方式是,要插入元素的前面元素都是有序的,基于这个特性,再找插入位置的时候,我们可以使用二分查找法。

代码实现:

#include<stdio.h>
#include<stdlib.h>
#define null NULL 

/*
	插入排序算法的基本思想:
		从数组的第二个元素开始,依次和前面的元素比较,找到要插入的位置,移动元素,插入。 
	插入排序的时间复杂度 O(n^2).
	
	在插入排序的三种算法中,带哨兵、不带哨兵、折半插入排序 无论做何种改进,该算法的时间复杂度都是O(n^2).
	空间复杂度O(1).
	插入排序算法是个稳定的排序算法。 
*/


//插入排序,不带哨兵的实现方式 
void insertSort(int a[],int n){
	//从第二元素开始,依次跟前面的元素比较 
	for(int i = 1;i < n;i++){
		//前一个元素大于后一个元素,无序,需要找到元素的插入位置 
		if(a[i] < a[i-1]){
			int temp = a[i];
			//移动元素
			int j; 
			for(j = i-1;j >= 0 && a[j] > temp; j--){
				 a[j+1]=a[j]; 	
			}
			a[j+1]=temp;
		}
	} 
}
//插入排序,带哨兵的实现方式
//a[] 数组中 a[0]位置为哨兵, 真正数据元素从a[1]开始, n为真实的数组中元素个数 
void insertSort2(int a[], int n){
	//从第二个位置开始
	for(int i = 2; i <= n;i++ ){
		if(a[i]<a[i-1]){
			a[0]=a[i];
			int j;
			//移动元素 
			for(j=i-1;a[j] > a[0];j--){
				a[j+1] = a[j];
			}
			a[j+1]=a[0];
		} 
	} 
	
} 

//折半插入排序 
void binarySearchInsertSort(int a[],int n){
	//插入排序前面的元素均是有序状态, 所以找插入位置我们可以采用二分查找法进行查找
	for(int i=1; i<n ; i++){
		//无序,需要交换 
		if(a[i]<a[i-1]){
			int temp = a[i]; 
			int low = 0; 
			int high = i-1;
			int mid;
			while(low <= high){
				mid = (low+high)/2;
				//为了保证插入排序的稳定性,当a[mid] == temp 时, 继续往后找即 low = mid+1; 
				if(a[mid]> temp){
					high = mid-1;	
				}else{
					low = mid+1;		
				} 
			}
			//将 [low,i-1]位置的元素后移 
			for(int j = i-1 ; j>=low ;j--){
				a[j+1]=a[j];
			} 
			//将要插入的元素放到指定位置
			a[low]=temp; 
			
		}

	} 
	
}

//插入排序 (基于链表的实现) 时间复杂度仍为O(n^2) 虽然移动元素的次数少了,但是比较关键字的数量级仍然是O(n^2)  
typedef struct LinkNode{
	int data;
	struct LinkNode *next; 
}LinkNode;

//创建一个链表 
void createLinkList(LinkNode *head, int a[],int n){
	for(int i = 0;i < n; i++){
		LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
		s->data = a[i];
		s->next = null;
		head->next = s;
		head=head->next;				
	}
}

//针对链表的插入排序    时间复杂度O(n^2) 
void insertSortLinkList(LinkNode *head){
	LinkNode *comp = head->next;
	//后面n-1个元素比较, 找到插入位置, 删除结点,并在指定的插入位置插入。 
	while(comp->next != null){
		LinkNode *start = head;
		int flag = 0;
		 while(start->next != comp->next){
		 	//为了保证插入排序的稳定性, 等号不能要 
 			if(comp->next->data < start->next->data){
 				flag = 1;
 				//printf("%d\n",comp->next->data);
 				//找到了要插入的位置
				//1.将原来的结点删除
				LinkNode *p = comp->next;
				comp->next=p->next;
				//加入到start指向结点的后面即可 
			 	p->next = start->next; 
			 	start->next= p;
			 	break;
			 }
			 start = start->next;
 		}
 		//flag == 0 代表没有元素被删除插入到某个位置,此时我们需要递进
		//flag == 1 代表有元素被删除并插入到前面的某个位置了,  此时comp不需要递进 
 		if(flag == 0){
 			comp = comp->next;
	 	}
	} 
}

//打印链表 
void printLinkList(LinkNode *head){
	while(head->next != null){
		printf("%d ",head->next->data);
		//别忘了让head指针后移 
		head = head->next;
	}
}

int main(int argc, char *argv[])
{
	int array[] = {4,12,1,17,3,24,11};
	//insertSort2(array,6);
	//for(int i  = 1;i <= 6;i++){
	//	printf("%d\n",array[i]);
	//}
	//头结点
	LinkNode *head = (LinkNode *)malloc(sizeof(LinkNode));
	createLinkList(head,array,7);
	printLinkList(head);
	printf("\n"); 
	insertSortLinkList(head);
	printLinkList(head); 
	return 0;
}

结果测试:

结论: 

插入排序无论是基于链表实现,还是基于数组实现,无论如何优化,时间复杂度均为O(n^2).

折半查找的插入排序,虽然找关键字的比较次数少了,但是移动元素的次数并没变,所以时间复杂度仍为O(n^2)级别.

基于链表的插入排序,虽然移动元素的时间复杂度变为了O(1).但是对比关键的次数并没少,所以时间复杂度仍为O(n^2)级别。

空间复杂度都是O(1).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值