【上机代码】插入排序——直接插入,折半插入,希尔排序

直接插入排序

思路

在排序中,序列为以下状态:

有序待排无序
L[1,…,i-1]L[i]L[i+1,…,n]

要将L[i]插入有序序列L[1,…,i-1]中
1,在L[1,…,i-1]中找到L[i]的插入位置K(从后向前遍历,K>=L[i])
2,将L[k,…,i-1]全部后移一位,空出L[k]
3,将L[i]插入L[K]

初始时,我们将L[1]看做有序,依次对L[2]-L[n]执行n-1次插入排序

代码

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void print(int A[], int length)
{
	for (int i = 0; i < length; ++i)
	{
		printf("%d ", A[i]);
	}
}
//直接插入排序
void InsertSort(int A[], int n) {
	int i, j, temp;
	for (i = 1; i < n; ++i)//从第二个元素开始遍历
	{
		if (A[i] < A[i - 1]) {//若当前遍历的元素>=前一元素,已经有序故不操作;若<前一元素,则执行
			temp = A[i];//暂存元素值
			for (j = i - 1; j >= 0&&A[j]>temp; --j)//从前一元素,向前遍历有序序列,直到遇到大于等于该元素的,或者遍历到头
				A[j + 1] = A[j];
			A[j + 1] = temp;//插在A[J]后面(若A[j]==temp,保证了稳定性)
		}
	}
}

void test()
{
	int A[10];
	int length = sizeof(A) / sizeof(A[0]);
	srand(time(0));
	for (int i = 0; i < length; ++i)
	{
		A[i] = rand() % 100;//取0-99的随机数  0-99,都为原值,100-199为0-99,以此类推

	}
	printf("原始序列为:\n");
	print(A, length);
	InsertSort(A, length);
	printf("\n排序后为:\n");
	print(A, length);
}


int main()
{
	test();
}

链表实现

思路

对数组插入排序时,是两重循环,第一重是从第二个元素开始遍历整个数组,第二个就是反向循环,从当前元素开始向前寻找第一个小于当前数的值,并插到它后面。

但单链表无法反向,所以第二重循环就可以进行正向遍历,找到第一个大于当前结点值的结点,并插到它前面。

借助两个指针sort和unsort,分别指向待排序的第一个元素,和未排序的最后一个元素,且unsort->next==sort。
1,当前元素大于链表第一个元素就直接头插法
2,当前元素不大于第一个元素,就从第二个元素开始遍历已排序的元素(利用p和q两个指针),要么已排序都小于当前元素,则插入尾部。要么找到大于当前元素的节点,插在该节点前即可。

错误点
1,unsort与sort对比是从第二个元素开始遍历,L->next->next开始

2,遍历要遍历完sort中最后一个元素,因为之前只和第一个元素对比过了。

代码

#include<stdio.h>
#include "malloc.h"
#include<stdlib.h>
#include<time.h>
//定义链表结构体
typedef struct LinkList
{
	int data;
	int id;//唯一标识每个节点
	struct LinkList* next;
}LinkList;

//交换链表中两个数据
void swap(LinkList* p, LinkList* min)
{
	int temp = p->data;
	p->data = min->data;
	min->data = temp;
};

void Insertsort(LinkList* L)
{
	LinkList* sort, * unsort, * p, * q;
	if (L != NULL) { //保证链表不为空
		sort = L->next;//指向排序好部分的最后一个元素
		while (sort->next != NULL)//遍历未排序的第一个元素
		{
			unsort = sort->next;//每次循环都指向未排序的第一个元素
			if (unsort->data < L->next->data) { //未排序的元素小于链表第一个元素
				sort->next = unsort->next;//头插法,把第一个未排序的插入表头
				unsort->next = L->next;
				L->next = unsort;
			}
			else
			{
				q = L->next;
				p = q->next;//从第二个元素开始遍历,第一个在上面头插法对比过了
				while (unsort->data >= p->data) {//直到找到p>unsort
					if (p->id == unsort->id)//遍历到unsort就停止,若遍历到unsort前一个就会少对比sort的最后一个元素
						break;
					q = p;
					p = p->next;
				}
				if (p->id == unsort->id) {//遍历到unsort前。说明未排序元素均大于已排序元素,插入队尾
					sort = unsort;
				}
				else//未遍历到unsort前,找到了大于未排序元素的p,插入p前
				{
					sort->next = unsort->next;
					unsort->next = p;
					q->next = unsort;
				}
			}
		}
	}
}

//输出链表节点
void print(LinkList* L)
{
	LinkList* p = L;
	for (int i = 0; i < L->data; ++i)//L为头结点不能动 否则每次遍历就会改变条件
	{
		printf("%d-%d ", p->next->id, p->next->data);

		p = p->next;
	}
}

void test()
{
	//建立链表
	LinkList* L = (LinkList*)malloc(sizeof(LinkList));
	if (L == NULL) {
		printf("内存分配不成功!\n");
	}
	else
	{
		L->data = 10;
		LinkList* p = L;
		srand(time(0));
		for (int i = 0; i < L->data; ++i)
		{
			p->next = (LinkList*)malloc(sizeof(LinkList));
			if (p->next) {
				p = p->next;
				p->data = rand() % 100;//取0-99的随机数  0-99,都为原值,100-199为0-99,以此类推
				p->id = i;
			}
		}
		p->next = NULL;
		printf("原始序列为:\n");
		print(L);
		Insertsort(L);
		printf("\n排序后为:\n");
		print(L);
	}
}

int main()
{
	test();
}

折半插入排序

思路

在直接插入排序的基础上做改进,以减少比较次数。

有序待排无序
L[1,…,i-1]L[i]L[i+1,…,n]

1,对已排序列L[1,…,i-1]使用折半查找法找到比待排元素L[i]大的元素L[k]
2,将L[k,…,i-1]全部后移一位,空出L[k]
3,将L[i]插入L[K]

代码

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void print(int A[], int length)
{
	for (int i = 0; i < length; ++i)
	{
		printf("%d ", A[i]);
	}
}
//折半插入排序
void InsertSort(int A[], int n) {
	int i, j, low,high,mid,temp;
	for (i = 1; i < n; ++i)//从第二个元素开始遍历
	{
		temp = A[i];//暂存数据
		low = 0; high = i - 1;//已排序序列首尾元素
		while (low <= high) {//折半查找
			mid = (low + high) / 2;//取中间元素
			if (A[mid] > temp)//不断缩小查找访问
				high = mid - 1;
			else
				low = mid + 1;//low会不断指向值更大的方向
		}
		若j<low,待排元素比前面所有元素都大,无需后移,直接插在原处。
		for (j = i - 1; j >= low; --j)//值大于temp的元素,依次后移
			A[j + 1] = A[j];
		A[low] = temp;//插入low位置
	}
}

void test()
{
	int A[10];
	int length = sizeof(A) / sizeof(A[0]);
	srand(time(0));
	for (int i = 0; i < length; ++i)
	{
		A[i] = rand() % 100;//取0-99的随机数  0-99,都为原值,100-199为0-99,以此类推

	}
	printf("原始序列为:\n");
	print(A, length);
	InsertSort(A, length);
	printf("\n排序后为:\n");
	print(A, length);
}
int main()
{
	test();
}

希尔排序

思路

插入排序适合基本有序的序列,若序列逆序时间复杂度达到 O ( n 2 ) O(n^{2}) O(n2),若为正序则可提升到O(n),希尔排序(缩小增量排序)就是在此基础上进行优化。
首先确定一个小于n的增量d,将序列分为形如L[i,i+d,…,i+kd]的d个子表。即间隔d的元素组成子表,然后对每个子表进行插入排序。不断缩小d的值,进行排序。当整个表已经基本有序(具有较好的局部有序性)的时候(d=1),最后进行一次排序即可。

代码

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void print(int A[], int length)
{
	for (int i = 0; i < length; ++i)
	{
		printf("%d ", A[i]);
	}
}
//希尔排序
void ShellSort(int A[], int n) {
	int d, i, j, k, temp;
	for (d = n / 2; d >= 1; d = d / 2)//取增量,以增量为间隔构建子表
	{
		for (i = 0; i < d; ++i)//遍历子表的头结点
		{
			for (j = i + d; j < n; j = j + d)//遍历每个子表的元素,默认第一个元素有序,按直接排序处理。
			{
				if (A[j] < A[j - d]) {
					temp = A[j];//暂存数据
					for (k = j - d; k >= 0 && temp < A[k]; k -= d)
						A[k + d] = A[k];
					A[k + d] = temp;
				}
			}
		}//每次都使整个序列更有序,从而直接排序速度越来越快。
	}
}

void test()
{
	int A[10];
	int length = sizeof(A) / sizeof(A[0]);
	srand(time(0));
	for (int i = 0; i < length; ++i)
	{
		A[i] = rand() % 100;//取0-99的随机数  0-99,都为原值,100-199为0-99,以此类推

	}
	printf("原始序列为:\n");
	print(A, length);
	ShellSort(A, length);
	printf("\n排序后为:\n");
	print(A, length);
}


int main()
{
	test();
}

参考:
#数据结构 将带头结点的单链表实现直接插入排序.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

燕南路GISer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值