当前位置:   article > 正文

数据结构之顺序表+常见面试题_面试中的顺序表操作

面试中的顺序表操作

数据结构分为两种

1、物理结构(内存中如何存储的)

2、逻辑结构(是我们想象出来的)

线性表(逻辑上连续)包括顺序表、链表、栈、队列。

顺序表在物理上和逻辑上都是连续的(其实就是数组)

静态顺序表的基本结构

  1. struct static_Array_list
  2. {
  3. int arr[10];
  4. int size;
  5. };

存在的问题

1、数据类型无法改变

2、数组大小无法改变

所以引入动态顺序表。

动态顺序表的基本结构

  1. typedef int SLDataType;
  2. typedef struct dynamic_Array_list
  3. {
  4. SLDataType *arr;//定义数组指针
  5. size_t size;//有效数据个数
  6. size_t capacity//容量
  7. }ArrayList;

缺点

1、顺序表在尾插尾删上很方便,但是在头(中间)插头(中间)删上复杂度为O(n)

2、空间利用率不高,增容会有一定空间浪费(批量增容)

优点

1、内存连续,随机访问

2、缓存命中率高(因为内存连续,预加载时把一段数据加载到缓存中,如果内存不连续,则会进行多次加载来获取所需要的的数据)

顺序表的基本接口

  1. // 基本增删查改接口
  2. // 顺序表初始化
  3. void ArrayListInit(ArrayList* psl, size_t capacity);
  4. // 顺序表销毁
  5. void ArrayListDestory(ArrayList* psl);
  6. // 顺序表打印
  7. void ArrayListPrint(ArrayList* psl);
  8. // 检查空间,如果满了,进行增容
  9. void CheckCapacity(ArrayList* psl);
  10. // 顺序表尾插
  11. void ArrayListPushBack(ArrayList* psl, SLDataType x);
  12. // 顺序表尾删
  13. void ArrayListPopBack(ArrayList* psl);
  14. // 顺序表头插
  15. void ArrayListPushFront(ArrayList* psl, SLDataType x);
  16. // 顺序表头删
  17. void ArrayListPopFront(ArrayList* psl);
  18. // 顺序表查找
  19. int ArrayListFind(ArrayList* psl, SLDataType x);
  20. // 顺序表在pos位置插入x
  21. void ArrayListInsert(ArrayList* psl, size_t pos, SLDataType x);
  22. // 顺序表删除pos位置的值
  23. void ArrayListErase(ArrayList* psl, size_t pos);

总的来说顺序表比较简单,因为底层是可以动态开辟空间的数组,基本接口的实现网上已经有很多相关的内容,不在赘述。

顺序表常见的面试题

1. 原地移除数组中所有的元素val,要求时间复杂度为O(N),空间复杂度为O(1)。

  1. //为了达到O(1)的空间复杂度(不能额外开辟数组长度大小的空间)
  2. //时间复杂度O(N)(只能遍历一次数组)
  3. //所以采用双指针的方法,用不重复的元素替换掉重复的元素
  4. //注意各种判断条件
  5. int removeElement(int* nums, int numsSize, int val){
  6. int head = 0;
  7. int end = numsSize-1;
  8. while(head<=end)//加=号的目的是防止最后双指针相遇,相遇点正好是要删除的数值
  9. {
  10. while(head<numsSize&&nums[head]!=val)
  11. {
  12. head++;
  13. }
  14. while(end>=0&&nums[end]==val)
  15. {
  16. end--;
  17. }
  18. if(head<end)
  19. {
  20. nums[head]=nums[end];
  21. head++;
  22. end--;
  23. }
  24. }
  25. return end+1;
  26. }

2. 删除排序数组中的重复项。

  1. //给排序数组元素去重
  2. //同样的,空间复杂度要求是O(1)
  3. //还是利用双指针的思想,从前往后替换
  4. //注意数组为空的情况
  5. int removeDuplicates(int* nums, int numsSize){
  6. if(numsSize==0)
  7. return 0;
  8. int head1 = 0;
  9. int head2 = 1;
  10. while(head2<=numsSize-1)
  11. {
  12. while(head2<=numsSize-1&&nums[head1] == nums[head2])
  13. {
  14. head2++;
  15. }
  16. if(head2<=numsSize-1)
  17. {
  18. nums[++head1] = nums[head2];
  19. }
  20. }
  21. return head1+1;
  22. }

3. 合并两个有序数组。

  1. void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
  2. //开辟额外的空间
  3. int *tmp = (int*)malloc(sizeof(int)*(m+n));
  4. //三个索引
  5. int index1=0;
  6. int index2=0;
  7. int index=0;
  8. while(index1<m && index2<n)
  9. {
  10. if(nums1[index1]<nums2[index2])
  11. {
  12. tmp[index++]=nums1[index1];
  13. index1++;
  14. }
  15. else
  16. {
  17. tmp[index++]=nums2[index2];
  18. index2++;
  19. }
  20. }
  21. //下面是将尾部的元素拷贝过来,可以优化,采用尾指针的方式
  22. //自动判断谁的尾部还有剩下的元素
  23. for(int i=index1;i<m;i++)
  24. tmp[index++]=nums1[i];
  25. for(int i=index2;i<n;i++)
  26. tmp[index++]=nums2[i];
  27. //拷贝给nums1
  28. for(int i=0;i<index;i++)
  29. nums1[i]=tmp[i];
  30. //整体来说时间复杂度O(2n+m),空间复杂度O(n+m)//可以优化
  31. }

4.旋转数组。

  1. //方法1.新建一个数组
  2. //该方法比较简单,但空间复杂度高O(N)
  3. void rotate(int* nums, int numsSize, int k){
  4. int* array_tmp = (int*)malloc(sizeof(int)*numsSize);
  5. k=k%numsSize;//值得注意的地方
  6. for(int i=0;i<k;i++)
  7. {
  8. array_tmp[i]=nums[numsSize-k+i];
  9. }
  10. for(int i = k;i<numsSize;i++)
  11. {
  12. array_tmp[i]=nums[i-k];
  13. }
  14. for(int i = 0;i<numsSize;i++)
  15. {
  16. nums[i] = array_tmp[i];
  17. }
  18. }
  19. //方法2.旋转数组
  20. //时间复杂度O(N),空间复杂度O(1)
  21. void Reverse(int* nums, int numsSize)
  22. {
  23. for(int i = 0; i < numsSize/2; i++)
  24. {
  25. int tmp = nums[i];
  26. nums[i] = nums[numsSize-i-1];
  27. nums[numsSize-i-1] = tmp;
  28. }
  29. }
  30. void rotate(int* nums, int numsSize, int k){
  31. k = k%numsSize;
  32. if(k!=0)//如果k==0,说明原来数组的没变
  33. {
  34. Reverse(nums,numsSize);
  35. Reverse(nums,k);
  36. Reverse(nums+k,numsSize-k);
  37. }
  38. }

5. 数组形式的整数加法。

  1. //逐位相加,然后逆转数组
  2. int* addToArrayForm(int* num, int numSize, int k, int* returnSize){
  3. *returnSize = 0;
  4. int* returnArray = (int*)calloc(fmax(10, numSize + 1),sizeof(int));
  5. while(k || numSize)
  6. {
  7. if(numSize)
  8. {
  9. returnArray[*returnSize] = returnArray[*returnSize] + num[numSize-1] + k%10;
  10. numSize--;
  11. }
  12. else
  13. {
  14. returnArray[*returnSize] = returnArray[*returnSize] + k%10;
  15. }
  16. if(returnArray[*returnSize] > 9)
  17. {
  18. returnArray[*returnSize] = returnArray[*returnSize]%10;
  19. returnArray[(*returnSize) + 1] = 1;
  20. if(!(k/10)&&!numSize)//防止最后一个数发生进位,数据的长度会增加
  21. (*returnSize)++;
  22. }
  23. k = k/10;
  24. (*returnSize)++;
  25. }
  26. for (int i = 0; i < (*returnSize) / 2; i++)
  27. {
  28. int tmp = returnArray[i];
  29. returnArray[i] = returnArray[(*returnSize) - 1 - i];
  30. returnArray[(*returnSize) - 1 - i] = tmp;
  31. }
  32. return returnArray;
  33. }

总的来说顺序表是非常基础的一种数据结构,用处也非常广泛,在后续的数据结构中也有很多衍生出来的结构是基于顺序表的。与之相反的一种数据结构是链表,链表和顺序表的特性恰恰相反,下篇文章共同学习线性表中的链表。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/829257
推荐阅读
相关标签
  

闽ICP备14008679号