线性表之顺序表

线性表之顺序表:

#define J_MaxSize 10 // 定义顺序表最大容量(用于静态分配)
#define InitSize 5  // 定义顺序表初始容量(用于动态分配)

typedef struct
{
    int data[J_MaxSize]; // 顺序表元素(静态分配)
    int length;          // 顺序表当前容量
} SqList;                // 顺序表类型定义

typedef struct
{
    int *data;     // 顺序表元素(动态分配)
    int length;    // 顺序表当前容量
    int D_MaxSize; // 顺序表最大容量
} SeqList;

// 顺序表动态分配初始化方式
L->data = (int *)malloc(sizeof(int) * InitSize);

// 按值查找(顺序查找)
// 在顺序表L中查找第一个元素值等于e的元素,并返回其次序
int LocateElem(SqList L, int e)
{
    for (int i = 0; i < L.length; i++)
        if (L.data[i] == e)
            return i + 1;
    return 0;
}

// 按位查找(顺序查找)
// 在顺序表L中查找第i个元素,并返回其值,(1 <= i <= length)
int GetElem(SqList L, int i)
{
    for (int j = 1; j < L.length; j++)
        if (j == i)
            return L.data[j];
    return 0;
}

// 在顺序表L的第i(1 <= i <= length+1)个位置插入新元素
bool ListInsert(SqList *L, int i, int e)
{
    if (i < 1 || i > L->length + 1) // 判断i的范围是否有效
        return false;
    if (L->length >= J_MaxSize) // 当前存储空间已满,不能插入
        return false;
    for (int j = L->length; j >= i; j--) // 将第i个元素及之后的元素后移
        L->data[j] = L->data[j - 1];
    L->data[i - 1] = e; // 在第i个位置处放入e
    L->length++;
    return true;
}

// 删除顺序表L中第i(1 <= i <= length)个位置的元素,用指针变量e返回
bool ListDelete(SqList *L, int i, int *e)
{
    if (i < 1 || i > L->length) // 判断i的范围是否有效
        return false;
    *e = L->data[i - 1];                // 将被删除的元素赋值给e
    for (int j = i; j < L->length; j++) // 将第i个位置后的元素前移
        L->data[j - 1] = L->data[j];
    L->length--;
    return true;
}
// 1. 从顺序表中删除具有最小值的元素(假设唯一),并由函数返回被删元素的值;
// 空出的位置由最后一个元素填补,若顺序表空,则退出
// 算法思路:扫描整个顺序表,查找最小值元素并记录其位置,扫描结束后用最后一个元素填补出原最小值元素的位置
bool test01(SqList *L, int *min)
{
    if (L->length == 0) // 判空表
        return false;

    *min = L->data[0]; // 假定0号元素值最小
    int pos = 0;       // 记录元素值最小的下标
    for (int i = 0; i < L->length; i++) 
    {
        if (*min > L->data[i]) // 寻找最小值元素
        {
            *min = L->data[i];
            pos = i;
        }
    }
    L->data[pos] = L->data[L->length - 1]; // 空出位置由最后一个元素填补(即移动最后一个元素的位置)
    L->length--;
    return true;
}
// 2. 将顺序表所有元素逆置,要求S(n)=O(1)
// 算法思路:扫描顺序表的前半部分元素,对于元素L.data[i] (0<=i<L.length/2),
// 将其与后半部分的对应元素L.data[L.length-1-i]进行交换
// 至于空间效率,保证算法原地工作,无论问题规模怎么变,算法运行所需的内存空间都是固定的常量即可
// SqList *L、int temp、int i
void test02(SqList *L)
{
    int temp; // 辅助变量
    for (int i = 0; i < L->length / 2; i++)  // key1:i < L->length / 2
    {
        temp = L->data[i];
        L->data[i] = L->data[L->length - 1 - i]; // key2: L->data[i] = L->data[L->length - 1 - i]; 
        L->data[L->length - 1 - i] = temp;
    }
}
// 3. 对长度为n的顺序表L,删除表中所有值为x的元素,要求T(n)=O(n),S(n)=O(1)
// 算法思路:用k记录顺序表中不等于x的元素个数(即需要保存的元素个数),扫描时将不等于x的元素移动到下标k的位置,并更新k值,扫描结束后修改L的长度
void test03(SqList *L, int x) // T(n)=o(n)、S(n)=O(1)
{
    int k = 0;                          // 记录值不等于x的元素个数
    for (int i = 0; i < L->length; i++) 
    {
        if (L->data[i] != x) 
        {
            L->data[k] == L->data[i]; 
            k++;                      // 不等于x的元素增1
        }
    }
    L->length = k; // 顺序表的长度等于k
}
// 4. 从有序顺序表中删除其值在给定值s与t之间(包含s和t自身,要求s<t)的所有元素,
// 若s或t不合理或顺序表为空,则退出
// 算法思路:特点是有序表,所以删除的元素必然是存储位置相连的整体,
// 先寻找值大于等于s的第一个元素(第一个删除的元素),然后寻找值大于t的第一个元素(最后一个删除的元素的下一个元素),
// 要将这段元素删除,只需将后面的元素前移
bool test04(SqList *L, int s, int t)
{
    if (s >= t || L->length == 0)
        return false;
        
    int i, j;

    for (i = 0; i < L->length && L->data[i] < s; i++) // 寻找值大于等于s的第一个元素
        if (i >= L->length)
            return false; // 所有元素值均小于s,返回

    for (j = i; j < L->length && L->data[j] <= t; j++); // 寻找值大于t的第一个元素
       
    for (; j < L->length; i++, j++)
        L->data[i] = L->data[j]; // 前移,填补被删元素位置
        
    L->length = i;
    return true;
}
// 5. 从顺序表中删除其值在给定值s与t之间(包含s和t自身,要求s<t)的所有元素,
// 若s或t不合理或顺序表为空,则退出
// 算法思路:特点是顺序表,从前向后扫描顺序表,用k记录下元素值在s到t之间元素的个数(初始时k=0),
// 对于当前扫描的元素,若其值不在s到t之间,则前移k个位置,否则执行k++,由于这样每个不在s到t之间的元素仅移动一次,因此算法效率较高
bool test05(SqList *L, int s, int t)
{
    if (s >= t || L->length == 0)
        return false;
    int i;
    int k = 0;
    for (i = 0; i < L->length; i++)
    {
        if (L->data[i] >= s && L->data[i] <= t)
        {
            k++;
        }
        else
        {
            L->data[i - k] = L->data[i]; // 当前元素前移k个位置
        }
    }
    L->length -= k; // 长度减小
    return true;
}
// 6. 从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同
// 算法思路:初始将第一个元素视为非重复的有序表,之后依次判断后面的元素是否与前面非重复有序表的最后一个元素相同,
// 若相同,则继续向后判断,若不同,则插入前面的非重复有序表的最后,直至判断到末尾
bool test06(SqList *L)
{
    if (L->length == 0)
        return false;
    int i, j;
    for (i = 0, j = 1; j < L->length; j++) // i 存储第一个不相同的元素,j为工作指针
        if (L->data[i] != L->data[j])      // 查找下一个元素与上个元素值不同的元素
            L->data[++i] = L->data[j];     // 元素前移,关键点在于++i而不是i++,目的是先执行i自增,再执行 L->data[++i] = L->data[j]; 
    L->length = i + 1;
    return true;
}
// 7. 将两个有序顺序表合并为一个新的有序顺序表,并由函数返回结果顺序表
// 算法思路:按顺序不断取下两个顺序表表头较小的结点存入新的顺序表中,然后看哪个表还有剩余,将剩下的部分加到新的顺序表后面
bool test07(SqList *L, SqList L1, SqList L2)
{
    if (L1.length + L2.length > L->length)
        return false;
    int i = 0, j = 0, k = 0;
    while (i < L1.length && j < L2.length) // 循环,两两比较,小者存入结果表
    {
        if (L1.data[i] <= L2.data[j])
            L->data[k++] = L1.data[i++]; // 先执行完 L->data[k++] = L1.data[i++],在执行k自增和i自增
        else
            L->data[k++] = L2.data[j++];
    }
    while (i < L1.length) // 还剩一个没有比较完的顺序表
        L->data[k++] = L1.data[i++];
    while (j < L2.length)
        L->data[k++] = L2.data[j++];
    L->length = k;
    return true;
}
// 8. 已知在一维数组A[m+n]中依次存放两个线性表(a1,a2,a3,...,am)和(b1,b2,b3,...,bn),
// 将数组中的两个顺序表的位置互换,即将(b1,b2,b3,...,bn)放在(a1,a2,a3,...,am)前面
// 算法思路,先将A[m+n]中的全部元素逆置,再将前n个逆置,再将后m个逆置,即可实现顺序表的位置互换
void Reverse(int A[], int left, int right, int arrSize)
{
    if (left >= right || right >= arrSize)
        return;
    int mid = (left + right) / 2; // key1
    for (int i = 0; i <= mid - left; i++)
    {
        int temp = A[left + i];
        A[left + i] = A[right - i]; // key2
        A[right - i] = temp;
    }
}
void test08(int A[], int m, int n, int arrSize)
{
    Reverse(A, 0, m + n - 1, arrSize); // A[m+n]原地逆置
    Reverse(A, 0, n - 1, arrSize);     // 对前n个元素逆置
    Reverse(A, n, m + n - 1, arrSize); // 对后m个元素逆置
}


// 9. 线性表(a1,a2,a3,...,an)中的元素递增有序且按顺序存储于计算机内,
// 用最少时间查找数值为x的元素,若找到,将其与后继元素位置相交换,
// 若找不到,则将其插入表中并保持数组递增有序
// 算法思路:顺序表递增有序,可折半查找、顺序查找,折半查找效率优于顺序查找
bool test09(SqList *L, int x)
{
    // 折半查找
    int low = 0, high = L->length - 1; // low和high指向顺序表下届和上届的下标
    int mid;
    while (low <= high)
    {
        mid = (low + high) / 2; // 找中间位置
        if (L->data[mid] == x)  // 找到x,退出while循环
            break;
        else if (L->data[mid] < x) 
            low = mid + 1;         // 左指针重置,到中点mid的右半部去查
        else
            high = mid - 1;        // 右指针重置,到中点mid的左半部去查
    }
    // 找到将其与后继元素交换
    if (L->data[mid] == x && mid != L->length - 1) // 若最后一个元素与x相等,则不存在与其后继交换的操作
    {
        int temp = L->data[mid];
        L->data[mid] = L->data[mid + 1];
        L->data[mid + 1] = temp;
    }
    
    if (low > high) // 查找失败,插入数据元素x
    {
        int i;
        for (i = L->length - 1; i > high; i--)
            L->data[mid + 1] = L->data[mid]; // 后移元素
        L->data[i + 1] = x;                  // 插入x
    }
}
// 10. 将n(n>1)个整数存放到一维数组R中,将R中保持的序列循环左移p(0<p<n)个位置,
// 即将R中的数据由(X0,X1,...,Xn-1)变换为(Xp,Xp+1,...,Xn-1,X0,X1,...,Xp-1),
// T(n),S(n)尽可能高效
// 算法思路:将数组ab转换为ba,a(数组的前p个数)、b(数组余下的n-p个数)
// 先将a逆置=>a(-1)b,再将b逆置=>a(-1)b(-1),最后将a(-1)b(-1)逆置=>ba
void test10(int A[], int n, int p, int arrSize) // p(0,n)
{
    Reverse(A, 0, p - 1, arrSize);
    Reverse(A, p, n - 1, arrSize);
    Reverse(A, 0, n - 1, arrSize);
}

// 11. 一个长度为L(L>=1)的升序序列S,处在第[L/2]个位置的数称为S的中位数。
// 例如,S1={11,13,15,17,19},S1的中位数是15,两个序列的中位数是含有他们所有元素的升序序列的中位数;
// 例如S2={2,4,6,8,20},则S1和S2的中位数是11,现有两个等长升序序列A和B,找出A和B的中位数
void test11(int A[], int B[], int n)
{
    // A、B等长升序序列
    int s1 = 0, d1 = n - 1, m1; // A的首位、末位、中位下标
    int s2 = 0, d2 = n - 1, m2; // B的首位、末位、中位下标
    //
    while (s1 != d1 || s2 != d2)
    {
        m1 = (s1 + d1) / 2;
        m2 = (s2 + d2) / 2;
        if (A[m1] == B[m2]) // 满足条件1
        {
            return A[m1];
        }
        if (A[m1] < B[m2]) // 满足条件2
        {
            if ((s1 + d1) % 2 == 0)
            {            // 若元素个数是奇数
                s1 = m1; // 舍弃A中间点以前的部分且保留中间点
                d2 = m2; // 舍弃B中间点以后的部分且保留中间点
            }
            else
            {                //若元素个数是偶数
                s1 = m1 + 1; // 舍弃A中间点及中间点以前部分
                d2 = m2;     // 舍弃B中间点以后的部分且保留中间点
            }
        }
        else // 满足条件3
        {
            if ((s2 + d2) % 2 == 0)
            {            // 若元素个数是奇数
                d1 = m1; // 舍弃A中间点以后的部分且保留中间点
                s2 = m2; // 舍弃B中间点以前的部分且保留中间点
            }
            else
            {                //若元素个数是偶数
                d1 = m1;     // 舍弃A中间点以后的部分且保留中间点
                s2 = m2 + 1; // 舍弃B中间点及中间点以前部分
            }
        }
    }
    return A[s1] < B[s2] ? A[s1] : B[s2];
}
// 12. 已知一个整数序列A=(a0,a1,...,an-1),其中0<=ai<n(0<=i<n);
// 若存在ap1=ap2=...=apm=x且m>n/2(0<=pk<n,1<=k<=m),则称x为A的主元素;
// 例如A={0,5,5,3,5,7,5,5},则5为主元素;
// 又如A={0,5,5,3,5,1,5,7},则A中没有主元素,假设A中的元素存在一个一维数组中,找出A的主元素
int test12(SqList L)
{
    int temp;
    int count = 1;
    int i;

    temp = L.data[0]; // 初始化候选主元素

    for (i = 0; i < L.length; i++) // 查找候选主元素
    {
        if (L.data[i] == temp)
        {
            count++; // 对候选主元素计数
        }
        else
        {
            if (count > 0)
            {
                count--; //
            }
            else
            {
                temp = L.data[i]; //更换候选主元素
                count = 1;
            }
        }
    }

    if (count > 0)
    {
        for (i = count = 0; i < L.length; i++) // 统计候选主元素的实际出现次数
        {
            if (L.data[i] == temp)
            {
                count++;
            }
        }
    }

    if (count > L.length / 2) // 确认候选主元素
        return temp;
    else
        return -1;
}
// 13. 给定一个含n(n>=1)个整数的数组,找出数组中未出现的最小正整数,要求时间高效,
// 例如{-5,3,2,3}中未出现的最小正整数是1,{1,2,3}中未出现的最小正整数是4
int test13(SqList *L)
{
    // 开辟一个新空间
    int *B = (int *)malloc(sizeof(int) * L->length);
    memset(B, 0, sizeof(int) * L->length);

    for (int i = 0; i < L->length; i++)
    {
        if (L->data[i] > 0 && L->data[i] <= L->length)
        {
            B[L->data[i] - 1] = 1; // 标记B
        }
    }
    for (int i = 0; i < L->length; i++) // 扫描数组B,找到目标值
    {
        if (B[i] == 0)
            break;
        return i + 1;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值