数据结构之线性表-顺序表的基本操作及其应用总结(带有详细注释及说明)

顺序表的基本操作

顺序表的定义及初始化

/*顺序表的定义*/
#include <stdio.h>
#include <malloc.h>
#define MAX_SIZE 100  //数组最大长度
typedef int ElemType; //数据类型的别名
//定义线性表结构体
typedef struct sqlist
{
    ElemType *data; //声明了一个名为data的长度不确定的数组,也叫“动态数组”
    int length;     //记录线性表的长度(线性表有给出表长度,适合用for循环进行操作)
} SqList;           //重命名的线性表的名称

//初始化顺序表,创建一个空表
void InitList(SqList *L)
{
    L->data = (ElemType *)malloc(MAX_SIZE * sizeof(ElemType)); //将申请的空间的首地址赋值给数组变量的第一个元素
    if (!L->data)
    {
        printf("存储空间分配失败!");
    }
    L->length = 0;
}

//--------------------------------------------
//测试的主函数
void main()
{
    //初始化顺序表
    SqList L;
    InitList(&L);
    printf("before:\n");
    for (int j = 0; j < 10; j++)
    {
        L.data[j] = j;

        printf("%d ", L.data[j]);
    }
    printf("\n");
}

顺序表的查找

按值查找

//*顺序表的按值查找: 从表头开始遍历,找到相等的元素则返回在表中的位置,由于数组下标由0开始,故应返回的位置序号为下标+1,没有找到则返回-1*/
#include "stdio.h"
#define MAX_SIZE 100  //数组最大长度
typedef int ElemType; //数据类型的别名
//定义线性表结构体
typedef struct sqlist
{
    ElemType *data; //声明了一个名为data的长度不确定的数组,也叫“动态数组”
    int length;     //记录线性表的长度(线性表有给出表长度,适合用for循环进行操作)
} SqList;

//L为代表顺序表的指针变量,e为查找的值
int FindByValue(SqList *L, int e)
{
    int i;
    for (i = 0; i < L->length; i++)
    {
        if (L->data[i] == e)
        {
            return i + 1;
        }
    }
    return -1;
}

注: 为什么没有按序号查找?

因为顺序表在高级语言中的实现就是数组,而按序号查找就只是一个加个数组下标的问题,没必要专门写一个函数。

顺序表的插入

/*在顺序表的第i个位置插入新结点e
实现步骤: 1: 将线性表的第i个至第n个结点后移一个位置.【注: 为避免元素丢失,故操作起点应从末尾元素开始后移】
         2:  将结点e插入到结点ai-1后
         3: 线性表长度+1
算法的时间复杂度为平均要移动表中一半的元素:O(n/2) = O(n)*/

#include "stdio.h"
#include <stdbool.h>
#define MAX_SIZE 100  //数组最大长度
typedef int ElemType; //数据类型的别名
//定义线性表结构体
typedef struct sqlist
{
    ElemType *data; //声明了一个名为data的长度不确定的数组,也叫“动态数组”
    int length;     //记录线性表的长度(线性表有给出表长度,适合用for循环进行操作)
} SqList;


//顺序表的插入
bool ListInsert(SqList *L, int i, int e)
{
    //容错判断
    if (i < 1 || i > L->length + 1)
    {
        return false;
    }
    if (L->length >= MAX_SIZE)
    {
        return false;
    }
    //(插入位置后的元素均后移一位,从尾元素开始移动)
    for (int j = L->length; j >= i; j--)
    {
        L->data[j] = L->data[j - 1];
    }
    //(在第i个位置,对应数组下标i-1,插入元素)
    L->data[i - 1] = e;
    //长度+1
    L->length++;
    return true;
}

顺序表按值/按位置删除

按值删除

/*顺序表的按值删除,删除值为x的第一个值
实现步骤:
    1:在顺序表L中查找值为x的第一个数据元素
    2:将从找到的位置的下一个元素开始至最后一个元素均向前移动一个位置。因为删除后,i位置有空挡,故操作起点应设置为第i+1个元素
    3:顺序表的长度减1*/
#include "stdio.h"
#include <stdbool.h>
#define MAX_SIZE 100  //数组最大长度
typedef int ElemType; //数据类型的别名
//定义线性表结构体
typedef struct sqlist
{
    ElemType *data; //声明了一个名为data的长度不确定的数组,也叫“动态数组”
    int length;     //记录线性表的长度(线性表有给出表长度,适合用for循环进行操作)
} SqList;

//删除值为x的第一个元素值(L为表的指针变量,x为删除的元素值)
bool Locate_Delete_SqList(SqList *L, int x)
{
    //遍历寻找值相同的元素
    int i = 0;
    while (i < L->length)
    { //值不同,则继续寻找
        if (L->data[i] != x)
        {
            i++;
        }
        else //值相同,找到了,其后的元素前移一个位置
        {
            for (int k = i + 1; k < L->length; k++)
            {
                L->data[k - 1] = L->data[k];
            }
            //长度-1,并跳出遍历循环
            L->length--;
            break;
        }
    }
    //容错判断
    if (i >= L->length)
    {
        printf("要删除的数据元素不存在!\n");
        return false;
    }
    return true;
}

按位置(序号)删除

/*顺序表的按位置删除
实现步骤:
    1:将顺序表第i+1个至第n个结点依次向前移动一个位置。因为删除后,i位置有空挡,故操作起点应设置为第i+1个元素
    2:顺序表长度减一*/
#include "stdio.h"
#include <stdbool.h>
#define MAX_SIZE 100  //数组最大长度
typedef int ElemType; //数据类型的别名
//定义线性表结构体
typedef struct sqlist
{
    ElemType *data; //声明了一个名为data的长度不确定的数组,也叫“动态数组”
    int length;     //记录线性表的长度(线性表有给出表长度,适合用for循环进行操作)
} SqList;

//L是链表指针变量,i用于指定删除的元素位置,*e用于保存删除的元素值
bool ListDelete(SqList *L, int i, int *e)
{
    //容错判断
    if (L->length == 0)
    {
        return false;
    }
    //容错判断
    if (i < 1 || i > L->length)
    {
        return false;
    }
    *e = L->data[i]; //保存删除的元素值
    //元素移动(第i+1个元素至末尾元素均前移一个位置)(i+1个元素对应数组下标i)
    for (int j = i; j < L->length; j++)
    {
        L->data[j - 1] = L->data[j];
    }
    //长度减一
    L->length--;
    return true;
}

顺序表的释放

/*原顺序表升序,将新元素x插入到适当位置,使其保持有序.(注意顺序表的插入/删除元素涉及后面数组元素的移动)*/
/*------------------------------------------------*/
/*释放顺序表*/
#include <stdio.h>
#include <malloc.h>
#define MAX_SIZE 100  //数组最大长度
typedef int ElemType; //数据类型的别名
//定义线性表结构体
typedef struct sqlist
{
    ElemType *data; //声明了一个名为data的长度不确定的数组,也叫“动态数组”
    int length;     //记录线性表的长度(线性表有给出表长度,适合用for循环进行操作)
} SqList;           //重命名的线性表的名称

//初始化顺序表,创建一个空表
void InitList(SqList *L)
{
    L->data = (ElemType *)malloc(MAX_SIZE * sizeof(ElemType)); //将申请的空间的首地址赋值给数组变量的第一个元素
    if (!L->data)
    {
        printf("存储空间分配失败!");
    }
    L->length = 0;
}

//释放顺序表
void DelList(SqList *L)
{
    free(L->data); //free释放了data指针指向的内存块,告诉系统该块内存闲置,但data仍然指向该内存块首地址,故需赋值为NULL;
    L->data = NULL;
    L->length = 0;
    printf("\nThe Sequential list has been freed!");
}

//-----------------------------------------

//test
void main()
{
    //初始化顺序表
    SqList L;
    InitList(&L); //上面方法在定义的时候形参是指针形式,说明传入地址,实参要用&符号传入普通变量的地址
    printf("before:\n");
    //初始化赋值顺序表
    for (int j = 0; j < 10; j++)
    {
        L.data[j] = j + 1;
        L.length++;

        printf("%d ", L.data[j]);
    }
    printf("\nlength: %d\n", L.length);
    //test(释放顺序表)
    DelList(&L);
    printf("\nL->data 's value: %s", L.data);
}

顺序表的应用示例

有序顺序表合并

要求:目的: 将两个升序顺序表A和B合并为新的升序表


算法思路:要合并两个有序的顺序表,需要新开启一个新的数组空间,

当A和B都未到末尾时,挨个比较A和B的元素,小元素放到C中,元素相等时,只保留一个;当A或B结束,将未结束表的元素逐个放入到C中

#include "stdio.h"
#include <stdbool.h>
#define MAX_SIZE 100  //数组最大长度
typedef int ElemType; //数据类型的别名
//定义线性表结构体
typedef struct sqlist
{
    ElemType *data; //声明了一个名为data的长度不确定的数组,也叫“动态数组”
    int length;     //记录线性表的长度(线性表有给出表长度,适合用for循环进行操作)
    int listsize;   //当前已分配的存储容量
} SqList;

//输入为: 三个表的首地址的指针变量
void Combination(SqList *L1, SqList *L2, SqList *L3)
{
    int i = 0, j = 0, k = 0;                       //三个用于指示三个顺序表中各自元素位置的变量,i对应L1,j对应L2,K对应合并的L3
    while ((i != L1->length) && (j != L2->length)) //当L1和L2表均不为空时,对应的各自元素进行比较
    {
        if (L1->data[i] > L2->data[j]) //L1中元素大,则将较小的L2元素合并到L3中
        {
            L3->data[k++] = L2->data[j++];
        }
        else if (L1->data[i] == L2->data[j]) //元素相等,选择只保留一个L2合并到L3中,则L1需跳过这个相等的元素
        {
            L3->data[k++] = L2->data[j++];
            i++;
        }
        else //L2中元素大,则将较小的L1元素合并到L3中
        {
            L3->data[k++] = L1->data[i++];
        }
        //长度加1
        L3->length++;
    }
    //当L1表还有剩余元素,直接合并到L3中
    while (i < L1->length)
    {
        L3->data[k++] = L1->data[i++];
        L3->length++;
    }
    //当L2表中还有剩余元素,直接合并到L3中
    while (j < L2->length)
    {
        L3->data[k++] = L2->data[j++];
        L3->length++;
    }
}

快排思想+左右指针

  • 题一:

要求:线性表中每个元素为不相等的整数。设计算法把所有奇数移到所有偶数的前面。


算法实现思想:

应用快排思想(左右指针套路),第一个元素只用来当中间变量,不作为比较的标准,比较的标准视题目而定

本题比较的标准是偶数,判断对应的元素是奇数还是偶数,使得奇数元素在左边,偶数元素在右边

#include <stdio.h>
//(array为传入的数组,n为表长)
void part(int array[], int n)
{
    int low = 0, high = n - 1; //快排思想,设置左右指针,分别指向第一个元素和最后一个元素
    int t = array[low];        //枢轴元素暂存,因为后面会被覆盖
    while (low < high)
    {
        //枢轴元素设置在左边,故一定要注意需要先从右边开始
        while (low < high && array[high] % 2 == 0) //high指针从右到左扫描,偶数不停,遇到奇数停下,交换到左边
        {
            high--;
        }
        array[low] = array[high];                 //找到了,直接覆盖左边,因为array[low]已经保存
        while (low < high && array[low] % 2 != 0) //low指针从左到右扫描,奇数不停,遇到偶数停下,交换到右边
        {
            low++;
        }
        array[high] = array[low]; //找到了,直接覆盖右边,因为本来上面的右边值已经赋值给左边了,覆盖无所谓
    }
    //将枢轴元素放到最终位置
    array[low] = t; //前面由于枢轴元素被覆盖加上已经保存了,现在需要将其放到相应位置上
}

//-----------------------------------

//test
void main()
{
    int array[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", array[i]);
    }
    part(array, 10);
    printf("\n");
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", array[i]);
    }
}
  • 题2:

要求:一个顺序表中的元素全部为正或负整数,设计算法,实现尽可能少时间将正负数分开,使所有负整数排在正整数前面!!


算法思想:

应用快排思想(左右指针套路),第一个元素只用来当中间变量,不作为比较的标准,比较的标准视题目而定

本题比较的标准是0,判断对应的元素是大于等于0还是小于0,使得负数元素在左边,大于等于0的元素在右边

#include <stdio.h>
//(array为传入的数组,n为表长)
void part(int array[], int n)
{
    int low = 0, high = n - 1; //快排思想,设置左右指针,分别指向第一个元素和最后一个元素
    int t = array[low];        //枢轴元素暂存,因为后面会被覆盖
    while (low < high)
    {
        //枢轴元素设置在左边,故一定要注意需要先从右边开始
        while (low < high && array[high] >= 0) //high指针从右到左扫描,大于等于0不停,遇到小于0停下,交换到左边
        {
            high--;
        }
        array[low] = array[high];            //找到了,直接覆盖左边,因为array[low]已经保存
        while (low < high && array[low] < 0) //low指针从左到右扫描,小于0不停,遇到大于0停下,交换到右边
        {
            low++;
        }
        array[high] = array[low]; //找到了,直接覆盖右边,因为本来上面的右边值已经赋值给左边了,覆盖无所谓
    }
    //将枢轴元素放到最终位置
    array[low] = t; //前面由于枢轴元素被覆盖加上已经保存了,现在需要将其放到相应位置上
}

//---------------------------------

//test
void main()
{
    int array[10] = {-1, 2, 3, 4, -3, -6, -7, 10, 12, -9};
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", array[i]);
    }
    part(array, 10);
    printf("\n");
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", array[i]);
    }
}

数组移位

数组移位问题,往往按照移位的k分成两段: 先将整体逆置,再将前一段逆置,最后将后一段逆置!

  • 题1:

设有一个线性顺序表存放在一维数组a[0,…,n-1]中,设计实现将数组中的每个元素循环右移k位,要求只用一个辅助单元,时间复杂度为O(n)!!


算法思想:

当循环移动k位,有k%n个尾部元素会移动到头部,而剩下的元素向后移动。在算法实现中,先将所有元素逆置,然后反转前k个元素,最后反转后n-k个元素,得到结果。

//实现基础,数组逆置(传入起始下标和终止下标的通用逆置)
void Reverse(int array[], int p, int q)
{
    //p为起始的下标,q为终止的下标
    for (; p < q; p++, q--)
    {
        //经典"借助一个中间变量的三步法"交换两个元素
        int temp = array[q];
        array[q] = array[p];
        array[p] = temp;
    }
}

//实现数组循环移位k位(n位表长,k为移位的个数)
void RightShift(int array[], int n, int k)
{
    k = k % n; //可能k>n,采用取余得到移动的位数
    //整体到局部,注意数组的下标从0开始,如最后一个元素为n-1
    Reverse(array, 0, n - 1); //整体逆置,对应下标从0到n-1
    Reverse(array, 0, k - 1); //将前一段即前k个元素逆置,对应下标从0到k-1
    Reverse(array, k, n - 1); //将后一段n-k个元素一段逆置,对应下标从k到n-1
}
  • 题2

要求:编写算法,在顺序表中查找指定的不位于表首的数据,查找成功则将该数据放到顺序表的最前面,而把其它位置元素后移一个位置


算法思路:1: 找元素 2:移元素

#include <stdio.h>
#include <malloc.h>
#define MAX_SIZE 100  //数组最大长度
typedef int ElemType; //数据类型的别名
//定义线性表结构体
typedef struct sqlist
{
    ElemType *data; //声明了一个名为data的长度不确定的数组,也叫“动态数组”
    int length;     //记录线性表的长度(线性表有给出表长度,适合用for循环进行操作)
} SqList;           //重命名的线性表的名称

//算法实现(L为地址传参,代表顺序表),(x为待查找的元素)
void searchValue(SqList *L, int x)
{
    //1.找元素
    for (int i = 0; i < L->length; i++)
    {
        //找到了,且其不位于句首
        if (L->data[i] == x && i != 0)
        {
            //2.移元素
            for (int j = i - 1; j >= 0; j--)
            {
                //i位置前面的元素都往后移动移位,从i-1下标位置开始后移
                L->data[j + 1] = L->data[j]; //从右向左看,代表了后移一位
            }
            //在表首位置插入找到的元素
            L->data[0] = x;
        }
    }
}

//初始化顺序表,创建一个空表
void InitList(SqList *L)
{
    L->data = (ElemType *)malloc(MAX_SIZE * sizeof(ElemType)); //将申请的空间的首地址赋值给数组变量的第一个元素
    if (!L->data)
    {
        printf("存储空间分配失败!");
    }
    L->length = 0;
}


//-------------------------------------

//test
void main()
{
    //初始化顺序表
    SqList L;
    InitList(&L); //上面方法在定义的时候形参是指针形式,说明传入地址,实参要用&符号传入普通变量的地址
    printf("before:\n");
    //初始化赋值顺序表
    for (int j = 0; j < 10; j++)
    {
        L.data[j] = j + 1;
        L.length++;

        printf("%d ", L.data[j]);
    }
    printf("\nlength: %d\n", L.length);
    printf("\n");
    //test
    searchValue(&L, 5); //上面方法在定义的时候形参是指针形式,说明传入地址,实参要用&符号传入普通变量的地址
    printf("after:\n");
    printf("length: %d\n", L.length);
    for (int i = 0; i < L.length; i++)
    {
        printf("%d ", L.data[i]);
    }
}

数组逆置

要求:实现整个顺序表中元素的就地逆置,即(a1,a2…an)变为(an,an-1…,a1)


算法思想:

要将顺序表逆置,即将对称元素交换.设顺序表的长度为n.将第i个元素与第n-(i-1)=n-i+1个元素交换,即可完成逆置,

注意,顺序表的下标从0开始,则在顺序表中第i个元素的存储下标为i-1,第n-i+1个元素下标为n-i

  • 实现方式一(单指针)(有局限,不通用):
/*实现数组的逆置,且要求使用原有空间,只需将对应的数组元素位置交换即可,
如第1个(对应下标是0)对应第n个位置(对应下标是n-1),第2个位置(下标1)对应n-2个位置(下标n-2);
基本规律为: 需要交换的两个元素的下标之和是n-1,其中n是表长
即i+j=n-1,故j=n-1-i*/
#include "stdio.h"
#include <stdbool.h>
#define MAX_SIZE 100  //数组最大长度
typedef int ElemType; //数据类型的别名
//定义线性表结构体
typedef struct sqlist
{
    ElemType *data; //声明了一个名为data的长度不确定的数组,也叫“动态数组”
    int length;     //记录线性表的长度(线性表有给出表长度,适合用for循环进行操作)
    int listsize;   //当前已分配的存储容量
} SqList;

//算法实现(地址传参)(普通结构体变量访问成员用.,指针变量型结构体变量访问成员用->)
int ListOppose(SqList *L)
{
    ElemType x;
    //只需要遍历数组的一般即可实现数据元素的交换
    for (int i = 0; i < L->length / 2; i++)
    {
        //经典"使用一个中间变量的三步交换法"来交换两个元素
        x = L->data[i];
        L->data[i] = L->data[L->length - 1 - i]; //数据元素交换位置,逆置
        L->data[L->length - 1 - i] = x;
    }
    return 1;
}
  • 实现方式二(双指针)(有局限,不通用):
#include "stdio.h"
#include <stdbool.h>
#define MAX_SIZE 100  //数组最大长度
typedef int ElemType; //数据类型的别名
//定义线性表结构体
typedef struct sqlist
{
    ElemType *data; //声明了一个名为data的长度不确定的数组,也叫“动态数组”
    int length;     //记录线性表的长度(线性表有给出表长度,适合用for循环进行操作)
    int listsize;   //当前已分配的存储容量
} SqList;

//算法实现(传入a顺序表,size为表长)
int ListOppose(int a[], int size)
{
    int tmp;
    //i从第一个元素开始,j从最后一个元素开始.循环条件为i<j
    for (int i = 0, j = size - 1; i < j; i++, j--)
    {
        //经典"使用一个中间变量的三步交换法"来交换两个元素
        tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }
}
  • 通用的数组逆置实现:
//注:此逆置算法的实现比上面更为通用
void Reverse(int A[], int left, int right, int arraySize)
{
    //容错条件
    if (left >= right || right >= arraySize)
    {
        return;
    }
    //确定中间位置
    int mid = (left + right) / 2;
    //遍历表长的一半即可完成对位交换
    for (int i = 0; i <= mid - left; i++)
    {
        //经典"使用一个中间变量的三步交换法"来交换两个元素
        int temp = A[left + i];
        A[left + i] = A[right - i];
        A[right - i] = temp;
    }
}

数组旋转

数组旋转问题,借助数组逆置实现。

要求:

已知在一维数组A[0,…,m+n-1]中依次存放着两个顺序表(a1,a2…,an)和(b1,b2,…,bn). 编写算法,将数组中两个顺序表的位置互换,即将(b1,b2,…,bn)放在(a1,a2…,an)的前面


算法思想:

数组旋转: 即AB变成BA。通过逆矩阵运算可得,

1: 可以先局部后整体: 即AB–> A-1B --> A-1B-1 --> (A-1B-1)-1 = BA

2: 也可先整体后局部: 即(AB)-1=B-1A-1 --> BA-1 --> BA

//算法实现基础,通用逆置的实现(left,right传入需要逆置范围的起始下标和终止下标)
//注:此逆置算法的实现比上面更为通用
void Reverse(int A[], int left, int right, int arraySize)
{
    //容错条件
    if (left >= right || right >= arraySize)
    {
        return;
    }
    //确定中间位置
    int mid = (left + right) / 2;
    //遍历表长的一半即可完成对位交换
    for (int i = 0; i <= mid - left; i++)
    {
        //经典"使用一个中间变量的三步交换法"来交换两个元素
        int temp = A[left + i];
        A[left + i] = A[right - i];
        A[right - i] = temp;
    }
}

//数组旋转算法实现
/*算法思想: 先整体后局部
1. 将A中所有元素逆置,变成bn,bn-1,...b1,am,am-1,...a1
2. 将前n个逆置
3. 将后m个逆置*/
void Exchange(int A[], int m, int n, int arraySize)
{
    Reverse(A, 0, m + n - 1, arraySize);
    Reverse(A, 0, n - 1, arraySize);
    Reverse(A, n, m + n - 1, arraySize);
}

其它例题

  • 题一

要求:

原顺序表升序,将新元素x插入到适当位置,使其保持有序.(注意顺序表的插入/删除元素涉及后面数组元素的移动)


算法实现:

由于涉及顺序表的元素插入,元素要后移,而后移要从结尾元素开始。结合需要遍历的需求,这里对列表从后往前的顺序遍历。

对顺序表从后往前遍历,寻找插入位置,若当前位置比x大,则向后移动一个位置,否则,将x插入该位置的下个位置【画图辅助理解】

#include <stdio.h>
#include <malloc.h>
#define MAX_SIZE 100  //数组最大长度
typedef int ElemType; //数据类型的别名
//定义线性表结构体
typedef struct sqlist
{
    ElemType *data; //声明了一个名为data的长度不确定的数组,也叫“动态数组”
    int length;     //记录线性表的长度(线性表有给出表长度,适合用for循环进行操作)
} SqList;           //重命名的线性表的名称

//算法实现。传入顺序表(地址传参),传入x代表待插入的元素值
void InsertSqList(SqList *a, int x)
{
    int i;
    //从后往前遍历,当前元素比x大,则后移一个单位
    for (i = a->length - 1; a->data[i] > x && i >= 0; i--) //注意i>=0不然会越界
    {
        a->data[i + 1] = a->data[i]; //从右向左看,代表了后移一位
    }
    //注意for循环体执行了,那么i--就会随着执行
    //故当for循环执行结束后,i指向了当前位置比x小的数组下标位置,那么在其下个位置插入x
    a->data[i + 1] = x;
    a->length++; //顺序表长度加1
}

//初始化顺序表,创建一个空表
void InitList(SqList *L)
{
    L->data = (ElemType *)malloc(MAX_SIZE * sizeof(ElemType)); //将申请的空间的首地址赋值给数组变量的第一个元素
    if (!L->data)
    {
        printf("存储空间分配失败!");
    }
    L->length = 0;
}


//-------------------------------------

//test
void main()
{
    //初始化顺序表
    SqList L;
    InitList(&L); //上面方法在定义的时候形参是指针形式,说明传入地址,实参要用&符号传入普通变量的地址
    printf("before:\n");
    //初始化赋值顺序表
    for (int j = 0; j < 10; j++)
    {
        L.data[j] = j + 1;
        L.length++;

        printf("%d ", L.data[j]);
    }
    printf("\nlength: %d\n", L.length);
    printf("\n");
    //test(插入数据保持有序)
    InsertSqList(&L, 0); //上面方法在定义的时候形参是指针形式,说明传入地址,实参要用&符号传入普通变量的地址
    printf("after:\n");
    printf("length: %d\n", L.length);
    for (int i = 0; i < L.length; i++)
    {
        printf("%d ", L.data[i]);
    }
}
  • 题2:

要求:

已知顺序表(a1,a2,…an)中元素升序排列.

设计算法:完成用最少时间在表中查找数值为x的元素;若找到,则将其与后继元素位置相交换;找不到,则将其插入表中,并使表中元素仍保持升序


算法思想:

顺序表中元素有序,有序前提下进行高效查找需要使用折半查找。

如果查找成功,那么交换x与其后继元素;查找不成功,则把大于x的元素 后移一个位置,然后在小于x的元素的下个位置插入x

#include <stdio.h>
#include <malloc.h>
#define MAX_SIZE 100  //数组最大长度
typedef int ElemType; //数据类型的别名
//定义线性表结构体
typedef struct sqlist
{
    ElemType *data; //声明了一个名为data的长度不确定的数组,也叫“动态数组”
    int length;     //记录线性表的长度(线性表有给出表长度,适合用for循环进行操作)
} SqList;           //重命名的线性表的名称

//算法实现
void SearchExchangeInsert(SqList *L, int x)
{
    //使用折半查找法
    int low = 0, high = L->length - 1; //初始化两个位置(第一个位置和最后一个位置)
    int mid;                           //初始化中点位置序号
    //折半方法查找元素
    while (low <= high)
    {
        mid = (low + high) / 2;
        if (L->data[mid] == x)
        {
            break;
        }
        else if (L->data[mid] < x)
        {
            low = mid + 1;
        }
        else
        {
            high = mid - 1;
        }
    }
    //如果顺序表中存在x,L->data[mid]代表了x,交换x和其后继元素
    if (L->data[mid] == x && mid != L->length - 1) //判断条件是: L->data[mid]就代表了x,且mid不能位于最后一个元素,否则没有后继元素
    {
        //经典"借用一个中间变量的三步法"交换元素
        int temp = L->data[mid];
        L->data[mid] = L->data[mid + 1];
        L->data[mid + 1] = temp;
    }
    //如果顺序表不存在x,则把大于x的元素后移一个位置,然后在小于x的元素的下个位置插入x
    if (low > high) //low>high代表了折半查找失败
    {
        int i;
        //顺序表插入元素需要从后面开始移动元素,故需要从后向前遍历
        for (i = L->length - 1; L->data[i] > x && i >= 0; i--) //注意i>=0不然会越界
        {
            L->data[i + 1] = L->data[i]; //从右向左看,代表了后移一位
        }
        //注意for循环体执行了,那么i--就会随着执行
        //故当for循环执行结束后,i指向了当前位置比x小的数组下标位置,那么在其下个位置插入x
        L->data[i + 1] = x;
        L->length++; //顺序表长度加1
    }
}

//初始化顺序表,创建一个空表
void InitList(SqList *L)
{
    L->data = (ElemType *)malloc(MAX_SIZE * sizeof(ElemType)); //将申请的空间的首地址赋值给数组变量的第一个元素
    if (!L->data)
    {
        printf("存储空间分配失败!");
    }
    L->length = 0;
}


//----------------------------------

//test
void main()
{
    //初始化顺序表
    SqList L;
    InitList(&L); //上面方法在定义的时候形参是指针形式,说明传入地址,实参要用&符号传入普通变量的地址
    printf("before:\n");
    //初始化赋值顺序表
    for (int j = 0; j < 10; j++)
    {
        L.data[j] = j + 1;
        L.length++;
        printf("%d ", L.data[j]);
    }
    printf("\nlength: %d\n", L.length);
    printf("\n");
    //test
    SearchExchangeInsert(&L, 11); //上面方法在定义的时候形参是指针形式,说明传入地址,实参要用&符号传入普通变量的地址
    printf("after:\n");
    printf("length: %d\n", L.length);
    for (int i = 0; i < L.length; i++)
    {
        printf("%d ", L.data[i]);
    }
}

欢迎评论点赞和关注个人公众号,感谢!!

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值