数据结构丨查找与排序

查找

概念

关键字:数据元素中某个数据项的值,用它可以标识一数据元素。

查找:根据给定的值,在查找表中确定一个其关键字等于给定值的记录或数据元素。

查找算法评价标准

  • 查找速度
  • 占用存储空间大小
  • 算法复杂度

平均查找长度(Average Search Length, ASL):为确定记录在查找表中的位置,需和给定值进行比较的关键字个数的期望值称为查找算法在查找成功时的平均查找长度。

对于含有n个记录的表,查找成功的平均查找长度为

ASL = \sum_{i=1}^{n}P_iC_i

例如对于顺序查找,有

\left\{\begin{matrix} P_i=\frac{1}{n}\\ C_i=n-i+1 \end{matrix}\right.

常用查找算法

顺序查找

/*
查找关键字等于key的元素在顺序表中的位置并返回,若没有找到则返回0
*/
typedef struct searchList
{
    ElementType* data;
    int length;
}searchList;

int Sequential_Search(searchList alist,ElementType key)
{
    alist.data[0]=key;
    int i=alist.length;
    while(alist.data[i]!=key)
    {
        i--;
    }
    return i;
}

折半查找

折半查找的ASL:

ASL_{bs}=log_2(n+1)-1

折半查找只适用于有序表,且限于顺序存储结构

/*
查找关键字等于key的元素在顺序表中的位置并返回,若没有找到则返回0
*/
typedef struct searchList{
    ElementType* data;
    int length;
}searchList;

int Binary_Search(searchList alist,ElementType key,int start,int end){
   if(start>end){
    return 0;
   }
   int mid=start+(end-start)/2;
   if(alist.data[mid]==key){
    return mid;
   }
   else if(alist.data[mid]>key){
    return Binary_Search(alist,key,start,mid-1);
   }
   else{
    return Binary_Search(alist,key,mid+1,end);
   }  
}

分块查找

除了查找表还需要建立一个索引查找表,将查找表分为若干个子表,对于每个子表建立一个索引项,包括关键字项(其值为该子表内的最大关键字)和指针项(指示该子表的第一个记录在表中的位置)

要求:查找表分块有序,即子表内部可以无序,但子表与子表之间有序

哈希查找

哈希查找(Hash Search)是一种通过哈希函数将数据存储在哈希表中,并利用哈希函数将要查找的数据映射到哈希表中的位置来进行查找的方法。哈希函数将数据转换为哈希值,这个哈希值可以用来计算出数据在哈希表中的存储位置。当进行查找时,利用同样的哈希函数将要查找的数据转换为哈希值,然后在哈希表中查找对应的位置。

优势:查找的时间复杂度为O(1),即常数时间。因为通过哈希函数计算的哈希值可以直接指示数据在哈希表中的位置,不需要逐个比较数据进行查找。

问题:不同的数据可能会计算出相同的哈希值,这种情况称为哈希冲突。为解决哈希冲突,可以使用开放寻址法或链表法等方式。


排序

排序:计算机程序设计中一种重要的操作,它的功能时将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列。

排序算法的种类:

  • 插入排序:直接插入排序、折半插入排序、希尔排序
  • 交换排序:快速排序、冒泡排序
  • 选择排序:简单选择排序、堆排序
  • 归并排序:链表二路归并
  • 基数排序:个十百位依次排序

插入排序

1、直接插入排序

与后面的希尔排序道理一样

2、折半插入排序

代码实现见上机作业

3、希尔排序

希尔排序的子序列不是逐段分割,而是将相隔某个增量的记录组成一个子序列,关键字较小的记录就不需要一步一步向前挪动,而是跳跃式的往前移。因此希尔排序算法的时间复杂度较直接插入排序低。 

searchList shell_Sort(searchList alist)
{
    /*gap是设置的查找间隔*/
    int gap[4] = { 7,5,3,1 };
    for (int k = 0; k < 4; k++)
    {
        int delta = gap[k];
        for (int i = delta; i < alist.length; i++)
        {
            int temp = alist.data[i];
            int j;
            for (j = i; j >= delta && alist.data[j - delta] > temp; j -= delta)
            {
                alist.data[j] = alist.data[j - delta];
            }
            alist.data[j] = temp;
        }
    }
    return alist;
}

(实际上是把直接插入排序的间隔由1改为了gap[k]) 

交换排序

1、快速排序

人类计算机十大经典算法之一,必须掌握。
基本思想:在待排序表L[1...n]中任取一个元素pivot作为枢轴,通过一趟排序将待排序表划分为独立的两部分,使得:
若枢轴在第k个位置上,则前k-1个元素小于枢轴,k+1及以后的所有元素大于等于枢轴。这个过程称为一趟快速排序
然后分别递归地对两个子表重复上述过程,直至每部分内只有一个元素或空为止,即所有元素放在了其最终位置上。

考点:第一趟排序之后序列是怎样的/程序填空/程序答题

C语言代码实现:

searchList quick_Sort(searchList alist, int start, int end)
{
    int pivot = alist.data[start];
    int low = start, high = end;
    if (low >= high) 
    {
        return alist;
    }
    while (low < high)
    {
        while (low < high && alist.data[high] >= pivot)
        {
            high--;
        }
        alist.data[low] = alist.data[high];
        while (low < high && alist.data[low] <= pivot)
        {
            low++;
        }
        alist.data[high] = alist.data[low];
    }
    alist.data[low] = pivot;
    //至此完成第一趟排序
    quick_Sort(alist, start, low - 1);
    quick_Sort(alist, low + 1, end);
    return alist;
}

2、冒泡排序

见C语言专栏

选择排序

1、简单选择排序

见C语言专栏

2、堆排序

堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大根堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小根堆。

C语言代码实现:

typedef struct _Heap
{
    int* data;
    int length;
}Heap;

Heap BuildMaxHeap(Heap aheap)
{
    for (int i = aheap.length / 2; i > 0; i--)
    {
        aheap = HeadAdjust(aheap, i);
    }
    return aheap;
}

Heap HeadAdjust(Heap aheap, int k)
{
    aheap.data[0] = aheap.data[k];
    for (int i = 2 * k; i <= aheap.length; i *= 2)
    {
        if (i < aheap.length && aheap.data[i] < aheap.data[i + 1])
        {
            i++;
        }
        if (aheap.data[0] >= aheap.data[i])
        {
            break;
        }
        else
        {
            aheap.data[k] = aheap.data[i];
            k = i;
        }
    }
    aheap.data[k] = aheap.data[0];
    return aheap;
}

归并排序

见数据结构专栏的线性表(一元多项式的二路归并)

基数排序

MSD排序、LSD排序MSD (Most Significant Digit)排序和LSD (Least Significant Digit)排序是两种常用的字符串排序算法。

MSD排序是一种递归的排序算法,它首先根据最高位进行排序,然后递归地对每个桶中的子数组进行排序。具体步骤如下:

  1. 将元素按照最高位分为多个桶。
  2. 对每个桶中的子数组进行递归排序。
  3. 将所有桶中的子数组按照桶的顺序依次合并。

LSD排序是一种非递归的排序算法,它从最低位开始对数组中的元素进行排序。它适用于字符串/数字位数长度不一的情况。具体步骤如下:

  1. 从最低位开始,将元素按照当前位的字符分为多个桶。
  2. 将每个桶中的字符串按照桶的顺序依次合并。
  3. 重复步骤1和步骤2,直到所有位都被处理完。

MSD排序和LSD排序都是基于桶排序的思想,只是处理字符串/数组的顺序不同。

下面以LSD排序为例用C语言代码实现算法:

int getMax(int arr[], int n)
{
    int max = arr[0];
    for (int i = 1; i < n; i++)
    {
        if (arr[i] > max)
        {
            max = arr[i];
        }
    }
    int digits = 0;
    while (max > 0)
    {
        digits++;
        max /= 10;
    }
    return digits;
}

void LSDRadixSort(int arr[], int n) {
    int digits = getMax(arr, n);
    int count[10], output[20];
    for (int i = 0; i < digits; i++) {
        // 初始化计数数组
        for (int j = 0; j < 10; j++) {
            count[j] = 0;
        }

        // 计算每个位的出现次数
        for (int j = 0; j < n; j++) {
            int digit = (arr[j] / (int)pow(10, i)) % 10;
            count[digit]++;
        }

        // 累加计数数组,得到每个位的起始索引
        for (int j = 1; j < 10; j++) {
            count[j] += count[j - 1];
        }

        // 根据每个位的值,将元素放到输出数组中
        for (int j = n - 1; j >= 0; j--) {
            int digit = (arr[j] / (int)pow(10, i)) % 10;
            output[count[digit] - 1] = arr[j];
            count[digit]--;
        }

        // 将输出数组复制回原数组
        for (int j = 0; j < n; j++) {
            arr[j] = output[j];
        }
    }
}

getMax函数用于计算数组中最大元素的位数,以确定需要进行多少次基数排序。

LSDRadixSort函数使用LSD基数排序算法对数组进行排序。


上机作业

1、顺序表实现折半插入

#include<stdio.h>
#include<stdlib.h>

#define MAX_NUM 20

typedef struct searchList
{
	int* data;
	int length;
}searchList;

void printList(searchList alist);
searchList Binary_insertSort(searchList alist);

int main()
{
	searchList alist;
	alist.data = (int*)malloc(MAX_NUM * sizeof(int));
	alist.length = 10;
	int data_0[10] = { 19,96,12,11,19,80,9,11,12,8 };
	for (int i = 0; i < 10; i++)
	{
		alist.data[i] = data_0[i];
	}
	printf("原顺序表的顺序为:");
	printList(alist);
	printf("排序之后:");
	printList(Binary_insertSort(alist));
	free(alist.data);
	return 0;
}

void printList(searchList alist)
{
	for (int i = 0; i < alist.length; i++)
	{
		printf("%d ", alist.data[i]);
	}
	printf("\n");
}

searchList Binary_insertSort(searchList alist)
{
	int low, high, mid;
	for (int i = 1; i < alist.length; i++)
	{
		int x = alist.data[i];
		low = 0;
		high = i - 1;
		while (low <= high)
		{
			mid = (low + high) / 2;
			if (x >= alist.data[mid])
			{
				low = mid + 1;
			}
			else
			{
				high = mid - 1;
			}
		}
		for (int j = i - 1; j > high; j--)
		{
			alist.data[j + 1] = alist.data[j];
		}
		alist.data[high + 1] = x;
	}
	return alist;
}

运行结果:
原顺序表的顺序为:19 96 12 11 19 80 9 11 12 8
排序之后:8 9 11 11 12 12 19 19 80 96

D:\专业学习\大二下\DS\DSweek12\Debug\DSweek12.exe (进程 16532)已退出,代码为 0。
按任意键关闭此窗口. . .

2、希尔排序的代码实现 

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include <stdio.h>
#define MAX_NUM 20

typedef struct searchList
{
    int* data;
    int length;
}searchList;

void printList(searchList alist);
searchList shell_Sort(searchList alist);

int main()
{
    searchList alist;
    alist.data = (int*)malloc(MAX_NUM * sizeof(int));
    alist.length = 10;
    int data_0[10] = { 19,96,12,11,19,80,9,11,12,8 };
    for (int i = 0; i < 10; i++)
    {
        alist.data[i] = data_0[i];
    }
    printf("原顺序表的顺序为:");
    printList(alist);
    printf("排序之后:");
    printList(shell_Sort(alist));
    free(alist.data);
    return 0;
}

void printList(searchList alist)
{
    for (int i = 0; i < alist.length; i++)
    {
        printf("%d ", alist.data[i]);
    }
    printf("\n");
}

searchList shell_Sort(searchList alist)
{
    /*gap是设置的查找间隔*/
    int gap[4] = { 7,5,3,1 };
    for (int k = 0; k < 4; k++)
    {
        int delta = gap[k];
        for (int i = delta; i < alist.length; i++)
        {
            int temp = alist.data[i];
            int j;
            for (j = i; j >= delta && alist.data[j - delta] > temp; j -= delta)
            {
                alist.data[j] = alist.data[j - delta];
            }
            alist.data[j] = temp;
        }
    }
    return alist;
}

 运行结果与上一问相同。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值