• 数据结构───顺序表(梦开始的地方)


       种一棵树最好的时间是十年前,其次是现在。

       我写博客只是便利自己。因为遗忘一种自然现象,在未来的某一天,我一定会对某些东西模糊,这时候就可以回来翻翻自己的博客。

     

    什么是顺序表? 

    简单来说,就是按顺序依次存储简单元素的线性数据结构数组就是顺序表,其次就是字符串

     如何实现顺序表

       创建一个数组分为两种,一种静态,一种动态。在c++中vector就是开辟一个动态数组。vector在c++中是一个对象,可以调用成员方法来进行对数组增删插改。今天既然是来学习顺序表底层逻辑,那就用c语言简单实现一下。

    1. typedef int SLDataType;//顺序表要存储的类型
    2. #define N 100
    3. //创建数据结构--顺序表--静态
    4. typedef struct SqeList {
    5. SLDataType a[N];
    6. int size; //记录当前数据个数
    7. }SL;
    8. //创建数据结构--顺序表--动态
    9. typedef struct SqeList {
    10. SLDataType* a;
    11. int size; //记录当前数据个数
    12. int capacity; //顺序表最大容量
    13. }SL;

    下面就动态顺序表各个功能的实现。

     首先初始化顺序表

    1. void SqeListInit(SL* ps) {
    2. ps->a =NULL;
    3. ps->size = 0;
    4. ps->capacity =0;
    5. }

     然后实现插入元素功能,只要添加数据就需要先检查内存够不够,于是先实现增容功能。

    1. //扩容
    2. void SqeListCheckCapacity(SL* ps) {
    3. if (ps->size == ps->capacity) {
    4. int newcapacity = ps->capacity == 0 ? SIZEINIT : 2 * ps->capacity;
    5. SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));
    6. if (tmp == NULL) {
    7. perror("no space to use:");
    8. exit(-1);
    9. }
    10. ps->a = tmp;
    11. ps->capacity = newcapacity;
    12. }
    13. }
    14. //插入元素
    15. void SqeListInsert(SL* ps, int pos, SLDataType x) {
    16. SqeListCheckCapacity(ps);
    17. assert(pos >=0 && pos <= ps->size);
    18. memmove(ps->a+pos+1, ps->a+pos, (ps->size - pos) * sizeof(SLDataType));
    19. ps->a[pos] = x;
    20. ps->size++;
    21. }

     插入功能完成了,那么头插尾插直接调用即可。

    1. //尾部插入一个数据
    2. void SqeListPushBack(SL* ps,SLDataType x) {
    3. /*SqeListCheckCapacity(ps);
    4. ps->a[ps->size] = x;
    5. ps->size++;*/
    6. SqeListInsert(ps, ps->size, x);
    7. }
    8. //顶部插入一个数据
    9. void SqeListPushFront(SL* ps,SLDataType x) {
    10. //SqeListCheckCapacity(ps);
    11. //int end = ps->size - 1;
    12. //while (end >= 0) {
    13. // ps->a[end + 1] = ps->a[end];
    14. // end--;
    15. //}
    16. //ps->a[0] = x;
    17. //ps->size++;
    18. SqeListInsert(ps, 0,x);
    19. }

     删除元素的话,只要将要删除的元素覆盖掉,可以使用memmove函数,当然也可以写个循环一个一个的移。

    1. //删除尾部数据
    2. void SqeListPopBack(SL* ps) {
    3. assert(ps->size > 0);
    4. ps->size--;
    5. }
    6. //删除顶部数据
    7. void SqeListPopFront(SL* ps) {
    8. assert(ps->size > 0);
    9. //法一:
    10. //int begin = 1;
    11. //while (begin > ps->size) {
    12. // ps->a[begin - 1] = ps->a[begin];
    13. // begin++;
    14. //}
    15. //ps->size--;
    16. //法二:
    17. memmove(ps->a, ps->a + 1, (ps->size - 1) * sizeof(SLDataType));
    18. ps->size--;
    19. }

     接下来实现一下查找,直接暴力枚举,遍历一遍顺序表所有元素。

    1. //查找
    2. int SqeListFind(SL* ps, SLDataType x) {
    3. for (int i = 0; i < ps->size; i++) {
    4. if (ps->a[i] == x) {
    5. return i;
    6. }
    7. }
    8. return -1;
    9. }

    对于修改功能,直接赋值就好啦。还有排序功能,各种排序方法,因地适宜。最后也是最重要的,用realloca开辟的空间要归还,避免内存遗漏 ,养成好习惯。

    1. //顺序表销毁
    2. void SqeListDestory(SL* ps) {
    3. free(ps->a);
    4. ps->a = NULL;
    5. ps->size = ps->capacity = 0;
    6. }

    这就是一个简单动态顺序表的实现了。检验所学知识当然就是刷题啦

    例题:1. 两数之和 - 力扣(LeetCode)

     题目要求在一个数组中返回两个数的下标,这两个数相加等于目标值。直接暴力枚举就好啦,如果相等就返回两数下标。

    1. int* twoSum(int* nums, int numsSize, int target, int* returnSize){
    2. *returnSize=2;
    3. int*ans=(int*)malloc(2*sizeof(int));
    4. for(int i=0;i
    5. for(int j=i+1;j
    6. if(nums[i]+nums[j]==target){
    7. ans[0]=i;
    8. ans[1]=j;
    9. return ans;
    10. }
    11. }
    12. }
    13. return NULL;
    14. }

     在c语言中返回一个数组需要自己开辟一个静态数组,并且就还要告诉调用方返还数组访问的大小,就是*returnSize的大小。

    暴力枚举的时间复杂度为O(n^2),可以用哈希表的方法将时间复杂度降到O(n)。在c++STL库中有一个unordered_map可以直接用,当然了也可以用数组模拟一个哈希表,这里有现成的就直接用了哈。

    1. class Solution {
    2. public:
    3. vector<int> twoSum(vector<int>& nums, int target) {
    4. unordered_map<int,int> hash;
    5. for(int i=0;isize();++i){
    6. auto it=hash.find(target-nums[i]);
    7. if(it!=hash.end()){
    8. return {it->second,i};
    9. }
    10. hash[nums[i]]=i;
    11. }
    12. return {};
    13. }
    14. };

      曾经有一位高人说过,过掉题目本身,比用什么方法更重要。好了,完成了第一题现在我也是刷过leetcode的人了,但想要不断巩固和熟练掌握所学内容,就采用刷题的方式,因为每一次通过就是给自己一次的正向反馈,等自己知识储备一定多的时候,再返回来试着用不同的方法解决,优化时间复杂度和空间复杂度。

  • 相关阅读:
    最小生成树 Minimum Spanning Tree
    策略模式在应用中的实践
    AI艺术‘美丑’不可控?试试 AI 美学评分器~
    【Linux operation 43】Linux 设置或修改系统时区
    F. Alex‘s whims Codeforces Round 909 (Div. 3) 1899F
    计算机网络 --- TCP与UDP协议
    软件设计师——知识产权与标准化
    ElementuiPlus的table组件实现行拖动与列拖动
    SpringBoot的shiro整合mybatis+druid数据源
    django基于python的疫情数据可视化分析系统--python-计算机毕业设计
  • 原文地址:https://blog.csdn.net/qq_43112916/article/details/133948143