动态顺序表的基本操作

上一篇文章静态顺序表的基本操作,实现了静态顺序表的基本操作,不难发现静态顺序表有诸多缺陷,首先,进程中,需要开辟一大块空间供动态顺序表存储数据用,如果存储的数据不多(结点不多),会浪费很多内存;如果开辟空间比较少,而存储数据较多,又会存在数据存不下的情况,这都不是我们想要的,建立在这样的基础上,动态顺序表的就出现了,先来看一下动态顺序表的结构体:

动态顺序表的定义:

typedef int DataType;
#define INIT_SIZE 10

typedef struct SeqList
{
    DataType* _array;    //数据块指针
    size_t _size;   //顺序表有效元素个数
    size_t _capicity;   //顺序表容量
}SeqListD;

typedef SeqListD* PSeqListD;

这里的数据块指针是数据在内存中存储的首地址,就像数组的地址一样。

动态顺序表的基本操作:

实现基于动态数组的顺序表的以下基本操作:

  • 初始化
  • 尾插
  • 尾删
  • 头插
  • 头删
  • 顺序表判空
  • 获取顺序表中有效元素的个数
  • 获取容量
  • 清空顺序表中的所有元素 (注意:不改变顺序表空间的大小)
  • 动态增容
  • 销毁
  • 任意位置插入元素
  • 任意位置删除元素
  • 打印动态顺序表
  • 在动态顺序表中查找元素data,找到返回下标,找不到返回-1
  • 顺序表遍历
  • 冒泡排序,升序和降序两种版本,用了函数指针
  • 选择排序,升序和降序两种版本,用了函数指针
  • 升序比较
  • 降序比较

具体代码实现如下:

#pragma once 

#include <stdio.h>
#include <assert.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>

//动态顺序表的定义

typedef int DataType;
#define INIT_SIZE 10

typedef struct SeqList
{
    DataType* _array;
    size_t _size;   //顺序表有效元素个数
    size_t _capicity;   //顺序表容量
}SeqListD;

typedef SeqListD* PSeqListD;

/****************************************/

//动态顺序表初始化
void SeqListDInit(PSeqListD pSeq);

//动态顺序表的尾插
void SqListDPushBack(PSeqListD pSeq, DataType data);

//动态顺序表尾删
void SeqListDPopBack(PSeqListD pSeq);

//动态顺序表的头插
void SeqListDPushFront(PSeqListD pSeq,DataType data);

//动态顺序表的头删
void SeqListDPopFront(PSeqListD pSeq);


//顺序表判空
int SeqListDEmpty(PSeqListD pSeq);


//获取顺序表中有效元素的个数 
int SeqListDSize(PSeqListD pSeq);

//获取顺序表的容量 
int SeqListDCapacity(PSeqListD pSeq);

// 清空顺序表中的所有元素 注意:不改变顺序表空间的大小 
void SeqListDClear(PSeqListD pSeq);

// 动态增容 
void CheckCapacity(PSeqListD pSeq);

// 销毁顺序表 
void SeqListDDestroy(PSeqListD pSeq);

// 顺序表任意位置插入元素 
void SeqListDInsert(PSeqListD pSeq, DataType pos, DataType data);

// 顺序表任意位置删除元素 
void SeqListDErase(PSeqListD pSeq, DataType pos);

//打印动态顺序表
void SeqListDPrint(PSeqListD pSeq);

//在动态顺序表中查找元素data,找到返回下标,找不到返回-1
int Find(PSeqListD ps, DataType data);

//顺序表遍历
void PrintSeqListD(PSeqListD ps);

//冒泡排序,升序和降序两种版本,用了函数指针
void BubbleSort(PSeqListD ps, int(*cmp)(const void *elem1, const void *elem2));

//选择排序,升序和降序两种版本,用了函数指针
void SelectSort(PSeqListD ps, int(*cmp)(const void *elem1, const void *elem2));

//升序比较
int CmpInAscendingOrder(const void *elem1, const void *elem2);

//降序比较
int CmpInDescendingOrder(const void *elem1, const void *elem2);

//二分查找 
int BinarySearch(PSeqListD ps, DataType data);

/****************************************/

//动态顺序表初始化
void SeqListDInit(PSeqListD pSeq)
{
    assert(pSeq);
    pSeq->_array = (DataType*)malloc(sizeof(DataType)*INIT_SIZE);
    if (pSeq->_array == 0)
    {
        printf("申请失败!\n");
        return ;
    }
    pSeq->_size = 0;
    pSeq->_capicity = INIT_SIZE;
}

//检查当前动态顺序表容量,不够,则申请空间
void CheckCapacity(PSeqListD pSeq)
{
    assert(pSeq);
    if (pSeq->_size >= pSeq->_capicity)
    {
        DataType *p = (DataType*)realloc(pSeq->_array, 2 * (pSeq->_capicity)*sizeof(DataType));
        if (p == NULL)
        {
            printf("内存申请失败1\n");
            return;
        }
        else
        {
            pSeq->_array = p;
            pSeq->_capicity = 2 * pSeq->_capicity;
        }

    }
}

//打印动态顺序表
void SeqListDPrint(PSeqListD pSeq)
{
    assert(pSeq);
    for (size_t i = 0; i < pSeq->_size; i++)
    {
        printf("%d", pSeq->_array[i]);

    }
    printf("\n");
}

//动态顺序表的尾插
void SeqListDPushBack(PSeqListD pSeq, DataType data)
{
    assert(pSeq);
    CheckCapacity(pSeq);
    pSeq->_array[pSeq->_size] = data;
    pSeq->_size++;
    printf("插入成功!\n");
}

//动态顺序表尾删
void SeqListDPopBack(PSeqListD pSeq)
{
    assert(pSeq);
    if (pSeq->_size == 0)
    {
        printf("顺序表为空,无法删除!\n");
        return;
    }
    pSeq->_size--;
}

//动态顺序表的头插
void SeqListDPushFront(PSeqListD pSeq, DataType data)
{
    assert(pSeq);
    CheckCapacity(pSeq);
    for (int i = pSeq->_size; i > 0;i--)
    {
        pSeq->_array[i] = pSeq->_array[i - 1];
    }
    pSeq->_array[0] = data;
    pSeq->_size++;
}

//动态顺序表的头删
void SeqListDPopFront(PSeqListD pSeq)
{
    assert(pSeq);
    if (pSeq->_size == 0)
    {
        printf("顺序表为空,无法删除!\n");
        return;
    }
    for (int i = 0; i < pSeq->_size; i++)
    {
        pSeq->_array[i] = pSeq->_array[i + 1];
    }
    pSeq->_size--;
}


//顺序表判空,为空返回true,否则返回false 
int SeqListDEmpty(PSeqListD pSeq)
{
    assert(pSeq);
    if (pSeq->_size == 0)
    {
        printf("顺序表为空!\n");
    }
    printf("顺序表不为空,顺序表共有%d个元素\n", pSeq->_size);
    return pSeq->_size;
}

// 顺序表任意位置插入元素 
void SeqListDInsert(PSeqListD pSeq, DataType pos, DataType data)
{
    assert(pSeq);
    CheckCapacity(pSeq);
    for (int i = pSeq->_size; i > pos; i--)
    {
        pSeq->_array[i] = pSeq->_array[i - 1];
    }
    pSeq->_array[pos] = data;
    pSeq->_size++;
}

// 顺序表任意位置删除元素 
void SeqListDErase(PSeqListD pSeq, DataType pos)
{
    assert(pSeq);
    if (pos <= 0 || pos >= pSeq->_size)
    {
        printf("输入为止不合法!越界!\n");
        return;
    }
    for (int i = pos; i < pSeq->_size-1; i++)
    {
        pSeq->_array[i] = pSeq->_array[i + 1];
    }
    pSeq->_size--;
}

//获取顺序表中有效元素的个数 
int SeqListDSize(PSeqListD pSeq)
{
    assert(pSeq);
    return pSeq->_size;
}

//获取顺序表的容量 
int SeqListDCapacity(PSeqListD pSeq)
{
    assert(pSeq);
    return pSeq->_capicity;

}

// 清空顺序表中的所有元素 注意:不改变顺序表空间的大小 
void SeqListDClear(PSeqListD pSeq)
{
    assert(pSeq);
    pSeq->_size =0;
}

// 销毁顺序表 
void SeqListDDestroy(PSeqListD pSeq)
{
    assert(pSeq);
    free(pSeq->_array);

    pSeq->_array = NULL;
    pSeq->_capicity = 0;
    pSeq->_size = 0;

}

//在动态顺序表中查找元素data,找到返回下标,找不到返回-1
int Find(PSeqListD pSeq, DataType data)
{
    assert(pSeq);
    if (pSeq->_size == 0)
    {
        printf("顺序表为空!\n");
    }
    for (int i = 0; i < pSeq->_size; i++)
    {
        if (pSeq->_array[i] == data)
            return i;
    }
    return -1;
}

//顺序表遍历
void PrintSeqListD(PSeqListD pSeq)
{
    assert(pSeq);
    for (int i = 0; i < pSeq->_size; i++)
    {
        printf("顺序表中的全部元素:%d\n", pSeq->_array[i]);
    }
}

//升序比较
int CmpInAscendingOrder(const void *elem1, const void *elem2)
{
    return *(int*)elem1 - *(int*)elem2;
}
//降序比较
int CmpInDescendingOrder(const void *elem1, const void *elem2)
{
    return *(int*)elem2 - *(int*)elem1;

}

//冒泡排序,升序和降序两种版本,用了函数指针
void BubbleSort(PSeqListD pSeq, int(*cmp)(const void *elem1, const void *elem2))
{
    int i = 0;
    int j = 0;
    // 设置一个标签,如果一次循环中flag值始终没改变
    //证明数组内元素已经有序
    int flag = 0;
    //参数检验
    assert(pSeq);
    for (i; i < pSeq->_size - 1; i++)
    {
        flag = 0;// 把flag重置为0,方便下一次循环里有没有交换元素
        for (j; j < pSeq->_size - 1 - i; j++)
        {
            if (cmp(&pSeq->_array[j], &pSeq->_array[j + 1]) > 0)
            {
                int tmp = pSeq->_array[j];
                pSeq->_array[j] = pSeq->_array[j + 1];
                pSeq->_array[j + 1] = tmp;

                flag = 1;
            }
        }
        if (flag == 0)
            break;
    }
}

//选择排序,升序和降序两种版本,用了函数指针
void SelectSort(PSeqListD pSeq, int(*cmp)(const void *elem1, const void *elem2))
{
    int i = 0;
    int j = 0;
    int pos = 0;
    assert(pSeq);
    for (i; i < pSeq->_size -1; i++)
    {
        pos = i;
        for (j = i + 1; j<pSeq->_size; j++)
        {
            if (cmp(&pSeq->_array[j], &pSeq->_array[pos]) < 0)
            {
                pos = j;
            }
        }
        if (i != pos)
        {
            int tmp = pSeq->_array[i];
            pSeq->_array[i] = pSeq->_array[pos];
            pSeq->_array[pos] = tmp;
        }
    }
}

//二分查找 
int BinarySearch(PSeqListD pSeq, DataType data)
{
    int left = 0;
    int right = 0;
    int mid = 0;
    assert(pSeq);

    right = pSeq->_size - 1;
    while (left <= right)
    {
        if (pSeq->_array[mid] > data)
            right = mid - 1;
        else if (pSeq->_array[mid] < data)
            left = mid + 1;
        else
            return mid;

    }
    return -1;
}

/****************************************/
//测试部分
void SeqListDTest()
{
    SeqListD p; 
    SeqListDInit(&p);

    SeqListDPushBack(&p, 1);
    SeqListDPushBack(&p, 2);
    SeqListDPushBack(&p, 3);
    SeqListDPushBack(&p, 4);
    SeqListDPrint(&p);

    SeqListDPopBack(&p);
    SeqListDPrint(&p);

    SeqListDPushFront(&p, 4);
    SeqListDPushFront(&p, 5);
    SeqListDPrint(&p);

    SeqListDPopFront(&p);
    SeqListDPrint(&p);

    SeqListDEmpty(&p);

    SeqListDInsert(&p, 2, 6);
    SeqListDPrint(&p);

    SeqListDErase(&p, 2);
    SeqListDPrint(&p);

    BinarySearch(&p, 3);

}
顺序表的优点及适用场景

在上两篇文章中,我们完成的静态顺序表以及动态顺序表的基本操作实现,现在我们来总结一下顺序表的整体内容。

原理
顺序表存储是将数据元素放到一块连续的内存存储空间,存取效率高,速度快。但是不可以动态增加长度

优点
1.尾插效率高,便于随机访问
2.cpu缓存利用率高
3.存取速度高效,通过下标来直接存储

缺点
1.不利于插入和删除操作
2.不可以增长长度

比如:插入或者删除一个元素时,整个表需要遍历移动元素来重新排一次顺序

适用场景
1.频繁的查找却很少的插入和删除操作
2.在进行尾插的时候用顺序表,因为相对于链表来说,顺序表进行尾插不需要进行遍历来找到最后一个位置,而链表则需要遍历。这样会影响程序运行的效率。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值