数据结构之顺序表+常见面试题
Posted TangguTae
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构之顺序表+常见面试题相关的知识,希望对你有一定的参考价值。
数据结构分为两种
1、物理结构(内存中如何存储的)
2、逻辑结构(是我们想象出来的)
线性表(逻辑上连续)包括顺序表、链表、栈、队列。
顺序表在物理上和逻辑上都是连续的(其实就是数组)
静态顺序表的基本结构
struct static_Array_list
{
int arr[10];
int size;
};
存在的问题
1、数据类型无法改变
2、数组大小无法改变
所以引入动态顺序表。
动态顺序表的基本结构
typedef int SLDataType;
typedef struct dynamic_Array_list
{
SLDataType *arr;//定义数组指针
size_t size;//有效数据个数
size_t capacity//容量
}ArrayList;
缺点
1、顺序表在尾插尾删上很方便,但是在头(中间)插头(中间)删上复杂度为O(n)
2、空间利用率不高,增容会有一定空间浪费(批量增容)
优点
1、内存连续,随机访问
2、缓存命中率高(因为内存连续,预加载时把一段数据加载到缓存中,如果内存不连续,则会进行多次加载来获取所需要的的数据)
顺序表的基本接口
// 基本增删查改接口
// 顺序表初始化
void ArrayListInit(ArrayList* psl, size_t capacity);
// 顺序表销毁
void ArrayListDestory(ArrayList* psl);
// 顺序表打印
void ArrayListPrint(ArrayList* psl);
// 检查空间,如果满了,进行增容
void CheckCapacity(ArrayList* psl);
// 顺序表尾插
void ArrayListPushBack(ArrayList* psl, SLDataType x);
// 顺序表尾删
void ArrayListPopBack(ArrayList* psl);
// 顺序表头插
void ArrayListPushFront(ArrayList* psl, SLDataType x);
// 顺序表头删
void ArrayListPopFront(ArrayList* psl);
// 顺序表查找
int ArrayListFind(ArrayList* psl, SLDataType x);
// 顺序表在pos位置插入x
void ArrayListInsert(ArrayList* psl, size_t pos, SLDataType x);
// 顺序表删除pos位置的值
void ArrayListErase(ArrayList* psl, size_t pos);
总的来说顺序表比较简单,因为底层是可以动态开辟空间的数组,基本接口的实现网上已经有很多相关的内容,不在赘述。
顺序表常见的面试题
1. 原地移除数组中所有的元素val,要求时间复杂度为O(N),空间复杂度为O(1)。
//为了达到O(1)的空间复杂度(不能额外开辟数组长度大小的空间)
//时间复杂度O(N)(只能遍历一次数组)
//所以采用双指针的方法,用不重复的元素替换掉重复的元素
//注意各种判断条件
int removeElement(int* nums, int numsSize, int val){
int head = 0;
int end = numsSize-1;
while(head<=end)//加=号的目的是防止最后双指针相遇,相遇点正好是要删除的数值
{
while(head<numsSize&&nums[head]!=val)
{
head++;
}
while(end>=0&&nums[end]==val)
{
end--;
}
if(head<end)
{
nums[head]=nums[end];
head++;
end--;
}
}
return end+1;
}
//给排序数组元素去重
//同样的,空间复杂度要求是O(1)
//还是利用双指针的思想,从前往后替换
//注意数组为空的情况
int removeDuplicates(int* nums, int numsSize){
if(numsSize==0)
return 0;
int head1 = 0;
int head2 = 1;
while(head2<=numsSize-1)
{
while(head2<=numsSize-1&&nums[head1] == nums[head2])
{
head2++;
}
if(head2<=numsSize-1)
{
nums[++head1] = nums[head2];
}
}
return head1+1;
}
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
//开辟额外的空间
int *tmp = (int*)malloc(sizeof(int)*(m+n));
//三个索引
int index1=0;
int index2=0;
int index=0;
while(index1<m && index2<n)
{
if(nums1[index1]<nums2[index2])
{
tmp[index++]=nums1[index1];
index1++;
}
else
{
tmp[index++]=nums2[index2];
index2++;
}
}
//下面是将尾部的元素拷贝过来,可以优化,采用尾指针的方式
//自动判断谁的尾部还有剩下的元素
for(int i=index1;i<m;i++)
tmp[index++]=nums1[i];
for(int i=index2;i<n;i++)
tmp[index++]=nums2[i];
//拷贝给nums1
for(int i=0;i<index;i++)
nums1[i]=tmp[i];
//整体来说时间复杂度O(2n+m),空间复杂度O(n+m)//可以优化
}
//方法1.新建一个数组
//该方法比较简单,但空间复杂度高O(N)
void rotate(int* nums, int numsSize, int k){
int* array_tmp = (int*)malloc(sizeof(int)*numsSize);
k=k%numsSize;//值得注意的地方
for(int i=0;i<k;i++)
{
array_tmp[i]=nums[numsSize-k+i];
}
for(int i = k;i<numsSize;i++)
{
array_tmp[i]=nums[i-k];
}
for(int i = 0;i<numsSize;i++)
{
nums[i] = array_tmp[i];
}
}
//方法2.旋转数组
//时间复杂度O(N),空间复杂度O(1)
void Reverse(int* nums, int numsSize)
{
for(int i = 0; i < numsSize/2; i++)
{
int tmp = nums[i];
nums[i] = nums[numsSize-i-1];
nums[numsSize-i-1] = tmp;
}
}
void rotate(int* nums, int numsSize, int k){
k = k%numsSize;
if(k!=0)//如果k==0,说明原来数组的没变
{
Reverse(nums,numsSize);
Reverse(nums,k);
Reverse(nums+k,numsSize-k);
}
}
//逐位相加,然后逆转数组
int* addToArrayForm(int* num, int numSize, int k, int* returnSize){
*returnSize = 0;
int* returnArray = (int*)calloc(fmax(10, numSize + 1),sizeof(int));
while(k || numSize)
{
if(numSize)
{
returnArray[*returnSize] = returnArray[*returnSize] + num[numSize-1] + k%10;
numSize--;
}
else
{
returnArray[*returnSize] = returnArray[*returnSize] + k%10;
}
if(returnArray[*returnSize] > 9)
{
returnArray[*returnSize] = returnArray[*returnSize]%10;
returnArray[(*returnSize) + 1] = 1;
if(!(k/10)&&!numSize)//防止最后一个数发生进位,数据的长度会增加
(*returnSize)++;
}
k = k/10;
(*returnSize)++;
}
for (int i = 0; i < (*returnSize) / 2; i++)
{
int tmp = returnArray[i];
returnArray[i] = returnArray[(*returnSize) - 1 - i];
returnArray[(*returnSize) - 1 - i] = tmp;
}
return returnArray;
}
总的来说顺序表是非常基础的一种数据结构,用处也非常广泛,在后续的数据结构中也有很多衍生出来的结构是基于顺序表的。与之相反的一种数据结构是链表,链表和顺序表的特性恰恰相反,下篇文章共同学习线性表中的链表。
以上是关于数据结构之顺序表+常见面试题的主要内容,如果未能解决你的问题,请参考以下文章