您现在的位置是:首页 >技术教程 >表插入排序算法_20230612网站首页技术教程

表插入排序算法_20230612

Jasonchen1224 2024-10-22 12:01:04
简介表插入排序算法_20230612

表插入排序算法_20230612

  1. 前言

插入排序过程一般涉及到元素比较和移动两类操作,插入排序的优化目标就是减少比较和移动次数。为了减少比较次数,人们开发出二叉查找插入排序,其比较过程采用折半查找,可大幅减少比较次数;为了减少移动次数,2路插入排序方法进入人们的视野;本文即将介绍的表插入排序,主要借助静态链表形式,借助改变数据域中的next指针(下标)关系最终完成排序任务。

  1. 表插入排序介绍

表插入排序的记录中需包含next域,它指向本记录的下一个有序记录的位置,整个排序过程仅对next的关系进行梳理,最终形成循环链表。具体看一个例子。

初始状态:

Index域012345678
Key域INT_MAX49386597761327(49)
Next 域10

为了插入方便起见,设数组中下标为0的分量为表头结点,并令表头结点记录的关键字取最大的整数INT_MAX,那么表头结点就可以作为整个循环链表的起点和终点,因为它被强制定义为头结点,而且作为最大值位于排序的最后位置,那么就自然而然地形成了环形静态链表。

首先将静态链表中下标为‘1’的分量和表头结点构成一个循环链表,然后将下标‘2’到’n’的分量按记录关键字非递减插入到循环链表中。仍以上面的关键字为例,后续插入循环链表的过程。

i=2, 关键字38插入过程,由于38<49,所以38需要插入到49关键字之前,恰好头结点指向49,所以需要断开链表,插入38值头结点和49之间。

Index域012345678
Key域INT_MAX49386597761327(49)
Next 域201

i=3,关键字65插入过程,由于65>49,所以它位于静态链表最后一个位置,它的next域指向头结点,形成环状静态链表。

Index域012345678
Key域INT_MAX49386597761327(49)
Next 域2310

i=4,关键字97的插入过程,逻辑同上,

Index域012345678
Key域INT_MAX49386597761327(49)
Next 域23140

i=5,关键字76的插入过程,由于65<76<97,所以76需要插入至65和97两个数字之间,插入完成后的循环链表

Index 域012345678
Key域INT_MAX49386597761327(49)
Next 域231504

i=6,关键字13插入过程,由于13<38,它是循环链表中最小的值,所以位于循环链表的第一个位置

Index 域012345678
Key域INT_MAX49386597761327(49)
Next 域6315042

i=7 关键字插入过程,13<27<39, 待插入元素位于13和39两个元素之间,

Index 域012345678
Key域INT_MAX49386597761327(49)
Next 域63150472

i=8关键字插入过程,由于49=(49),所以它居于49元素之后,

Index 域012345678
Key域INT_MAX49386597761327(49)
Next 域681504723
  1. 表插入数据结构

表插入需要利用静态链表对元素的下标操作,形成一个非递减的循环静态链表。首先需要确定基本的数据结构,数据结构确定以后,才能更有效地利用编程对数据进行操作。定义静态链表每个结点包含两个元素,其一为基本的数据元素RcdType,其二是next指针,它是一个静态指针,储存指向下一个元素的数组下标。

基于上述数据元素定义,然后定义经典链表结构体,它包含一个栈形式的数组,它用于储存基本元素和元素之间的顺序逻辑关系,另外一个元素定义链表中实际所包含的元素数量。

#define MAX_LEN  15
#define MAX_SIZE 20
#define SIZE     100

typedef int   KeyType;
typedef char* Record;


typedef struct  RcdType
{
    KeyType key;
    Record  value;
} RcdType;


typedef struct SLNode
{
    RcdType rc;  //record item, rc indicates 'record'
    int     next; // next indicates the index THIS record points to
} SLNode; //Definition of static link list node

typedef struct SLinkListType
{
    SLNode r[SIZE];
    int    length; //current length of static link list
} SLinkListType;

  1. 表插入排序算法程序实现

表插入排序的操作对象为表中的元素以及元素之间的下标关系,由于每个元素都需要进行排序,所以外部循环至少需要进行(n-1)次,内部循环是对元素下标之间的关系进行操作,利用循环寻找至当前元素需要插入的合适位置,然后更新下标即可。为了方便操作,定义pre变量记录当前寻找元素的前一位置,定义cur为当前寻找元素所在位置,通过比较操作,最终寻找得到插入的合适位置。

值得一提的是,每次搜索当前元素所在位置,pre的值需要从下标为0的位置开始,cur代表的为循环链表中的实际第一个元素的值。

另外值得关注的点是,程序开始之前,需要先对头结点和第一个元素进行next这两个元素形成一个简单的循环链表,否则后面的比较操作将进入死循环,导致程序栈崩溃。

通过while循环比较,找到cur的前置元素pre,后面通过更新cur和pre所在位置元素的下标,从而实现表插入排序。

void list_insert_sort(SLinkListType *s_list)
{
    int i;
    int pre;
    int cur;

    s_list->r[0].next=1;
    s_list->r[1].next=0;

    for(i=2;i<=s_list->length;i++)
    {
        pre = 0; //s_list->r[0].next;
        cur = s_list->r[pre].next;

        while(LQ(s_list->r[cur].rc.key,s_list->r[i].rc.key))//search from the start
        {
            pre=cur;
            cur = s_list->r[pre].next;
        }

        s_list->r[i].next=s_list->r[pre].next;
        s_list->r[pre].next=i;
    }
}

我们通过上述程序排序后,可以发现表插入算法排序的结果为有序表,则只能对它进行顺序查找,不能随机查找,为了实现随机查找(折半查找),则需要对记录进行重新排序,记录重新排序的过程理解起来比较复杂,具体的图示实现过程可以参考《数据结构》(严蔚敏)的教材,在此不再赘述。

我们呈现C语言的代码给到读者,请自行理解。


void list_sort_arrange(SLinkListType *s_list)
{
    int i;
    int p;
    int q;
    SLNode temp;

    p=s_list->r[0].next;

    for(i=1;i<s_list->length;i++)
    {
        while(p<i)
        {
            p=s_list->r[p].next;
        }

        q=s_list->r[p].next;

        if(p!=i)
        {
            temp=s_list->r[i];
            s_list->r[i]=s_list->r[p];
            s_list->r[p]=temp;
            s_list->r[i].next=p; //Keet track of next element for next search
        }

        p=q;
    }

    return;
}
  1. 小结

本文回顾了表插入算法的实现方法以及重新排序的算法,加深了对静态链表的理解,同时也对表插入算法的实现机理有了进一步的理解。

参考资料:

  • 《数据结构》(严蔚敏)
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。