手撕顺序表

博主关于C语言的博客就先告一段落了,以后更难的数据结构在等着我们!!!!!!

在讲顺序表之前,我们要先引入一个概念,那就是线性表

1.线性表

线性表,是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表(我们本片博客主讲的内容)、单链表(博主正在学的),栈,队列,字符串。
线性表在逻辑上是线性结构,也就是说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常是数组和链式结构的形式存储。
通俗的说,如果存储的数据随着下标的增大,地址也增大的话,那么就是属于物理结构上的线性,例如我们所学的数组,可以通过下标来访问数组中的各个元素(这个成立的前提条件就是这个数组中的数据的物理结构是线性的)
而什么是逻辑结构线性呢?这里博主画个图帮助大家理解

例如我们家里的东西丢得杂乱无章,这时我们可能会用一个东西把它们规整起来,例如
在这里插入图片描述

我们使用了一个柜子来存放这些毫无关联的杂物,使得它们在柜子的前提下看起来有序了起来,我们这种人为想象出来的,对无关联事物用一个类似框架绑定起来,看上去显得有逻辑,这就是逻辑结构,所以逻辑结构一定是线性的。

顺序表

在讲完了线性表之后,顺序表的结构我们就可以有清晰的了解了
顺序表:
逻辑结构:线性的
物理结构:线性的(也就是说顺序表中的数据在地址上一定是连续的,所以我们可以用下标来访问顺序表的数据)

顺序表和数组的关系是什么?这个问题可有趣多了!!!我会举例子来帮助大家更好的理解这个问题

(1)我们平常想吃炒西兰花,去那种普通的餐馆,炒西兰花就叫炒西兰花。而要是我们去高档餐厅吃西兰花呢?你翻开菜单,你就会发现炒西兰花变成了另外一种高大上的名字———绿叶仙踪。这个绿野仙踪就是在西兰花的基础上,给他增添一点料汁,小饰品,以及各种各样的摆盘,当然,价格也就贵了很多
(2)再比如,我们想吃玉米羹,普通的餐厅肯定就叫玉米羹,但在高档的米其林餐厅呢?可能就叫做宫廷玉液羹,也是在玉米羹的基础上,加上点料汁,摆盘啥的
所以,数组和顺序表也是这样的一种关系,只不过我们无法对数组进行元素删除,元素插入等等操作,而顺序表就是在数组的基础上,增加了查找数据,删除数据等操作

以上是重点,请大家务必理解清楚。!!!!!!!!!!!!!!!
顺序表的三元素
1.指向内存空间的arr
2.顺序表中的有效数据个数size
3.顺序表的容量capacity

在这里插入图片描述

初始化顺序表

都说顺序表有三元素,那么前面C语言我们哪里学到了这样的一种变量来整合不同类型的变量呢?没错,就是结构体变量,为了防止有人忘记了这个知识点,我们来复习一下结构体的相关知识吧
结构体需包含struct关键字,比如我们想定义一个关于学生的结构体变量,这里包含了学生的名字,学生的年龄,学生的成绩等

struct Stu
{
    char name[];//存放学生名字的数组
    int age;//存放学生的年龄
    float score;//存放学生的成绩
};
int main()
{
    struct Stu st;//创建st变量
    st.name = "lisi";//对st这个结构体变量中的name进行初始化
    st.age = 16;//对st这个结构体变量中的age进行初始化,为16
    st.score = 98.5;//对这个结构体表变量中的score进行初始化,为98.5
    return 0;
}

以上就是博主对结构体进行的一个简单复习

接下来,我将通过画图,为大家直观地展示结构体各个成员的作用
在这里插入图片描述

顺序表的初始化

我们先对顺序表进行初始化,方便我们后面对其的操作,初始化指针为NULL,size有效数据为0,capacity容量也为0。即:

typedef int SLdatatype
typedef struct Seplist
{
   SLdatatype * arr;
   int size;
   int capacity;
}SL;
void SLinit(SL * ps)
{
   ps->arr = NULL;
   ps->size = 0;
   ps->capacity = 0;
}
void test01()
{
   SL sl;
   SLinit(&sl);
}
int main()
{
   test01();
   return 0;
}

上面的代码得有一定的功底,才能看得懂,我先对几行有意义的代码进行讲解,其他有不懂的可以私信博主

1.typedef int SLdatatype

这行代码的意思就是将int转化为SLdatatype,也就是说打完这段代码后,SLdatatype a,就相当于int a。
typedef关键字有重命名的作用,就是将一些复杂的关键字转化为我们自定义的,易操作打出来的字母。
我们知道,数组可以存放多种数据类型,那么,顺序表也一样,要是我们只是单纯的在结构体的定义上写 int * arr,那么后面的代码都得是int型,要是我们想使用顺序表存放float变量,就得一行一行地改类型,所以,我们干脆typedef int SLdatatype,以后想改类型直接把int改成我们想要的类型即可。就不用大费周章地一行一行的改代码

2.typedef struct Seplist
{
…;
}SL;

一般来说,结构体的名称都是很长的,我们呢想对结构体进行typedef操作(即对结构体进行改名操作)该怎么办呢,其实,先在struct…前面加上typedef即可,即typedef struct…,再在下面的大括号后加上你想重新定义的名字即上方的SL

3.int size
int capacity

这里我猜大家的疑问应该是为什么不跟上面arr一样,使用SLdatatype指针。我再说一遍,size和capacity是有效数据个数,是真真正正的整型类型,是多少个有效个数,有多大的容量!!!大家不可以搞混了

增容

大家有没有想过,我们一开始初始化的顺序表size和capacity全都是0,那这样难道不是近似于一个空表吗?所以在对顺序表进行任何操作的时候,我们都应该增容(空间有限另当别论),那么,该如何进行增容呢?
这里的操作涉及到动态内存管理函数,还没学习的可以看我的博客https://editor.youkuaiyun.com/md/?articleId=146161440
————————我们直接把代码给大家般过来,再一一进行讲解
在这里插入图片描述
如图,就是增容操作的具体代码
增容,无非就是增加capacity,我们的目的就是把capacity增加到4(这个数字随大家而定),可别忘了,capacity只是一个表示容量的数字,用来客观表示的而已。真正能做到扩展容量的还得是使用arr指针,再配合上realloc函数进行扩展
咱们先来看第一段代码。if(ps->size == ps->capacity)

这里其实就是代码容量满了的意思,就是顺序表存储的是数据个数已经达到了capacity的极限,例如
在这里插入图片描述

上面我们提到了,arr指向这块内存空间,所以我们通过realloc函数来对arr指向的空间进行扩容即可
realloc(void * ,size_t size):第一个参数指向要扩容的空间的地址,第二个是要将此指针所指向的空间扩展成多大字节的空间,用capacity*sizeof(SLdatatype)就是扩展后的内存大小。

接下来我们来看一行神之一手的代码!!!!
int newcapacity = capacity ==0?4:2*capacity;
这里先说明一下,因为增加capacity不好增加,所以先用newcapacity来代替capacity增加后的值,再通过复制把newcapacity的值传给capacity

capacity ==0?4:2*capacity

这里使用到了三目操作符。A?B:C。即A如果为真,那这个式子整体取B,若A为假,那这个式子整体就等于C
这里的意思就是在说明
->(1.)若capacity为0,那么就让它等于4,然后赋值给newcapacity(后续再通过赋值,将newcapacity的值赋给capacity,也就是说,后续capacity将会得到4
->(2.)若capacity不等于0,就比如容量capacity等于4,然后已经被用满了,现在要将capacity扩大两倍,就走的上面C的值,也就是2*capacity,最后会通过newcapacity赋值给capacity,也就是8.

现在capacity增加完毕,接下来需要怎么操作呢?
就是使用realloc给arr指向的内容增容
SLdatatype * tmp = (SLdatatype*)realloc(ps->arr,sizeof(SLdatatype)*newcapacity)

(1)这里使用tmp接收的原因是,realloc开辟空间可能会失败,失败了,那个指向内存的指针会变成NULL,会导致我们先前再顺序表里面存放的数据将会丢失,所以用中介tmp来接收,如果其为NULL,那就退出程序,如果它不为NULL,那就将其赋值给ps->arr。是这么个逻辑。

(2)这里的sizeof(SLdatatype)newcapacity,就是代表我们进行增容操作开辟内存后的字节数,newcapacity是代表可以存放多少个数据,sizeof(SLdatatype)就表示一个数据的字节数,它们相乘就表示内存空间的总字节数。eg.这里SLdatatype我们取int,那么sizeof(int) newcapacity=4*8就等于32(字节);
(3)最后,再将tmp赋给ps->arr指针,newcapacity赋给capacity。

自此,增容部分我们就讲完了。

顺序表是一个比较复杂的数据结构,所以我分成了三集,这里是第一集,往后我会写出更多的博客,帮助大家更好的理解顺序表

### 华为OD模式下的链表代码问题 在华为OD模式的面试中,链表作为经典的数据结构之一,经常被用来考察候选人的基本功以及对复杂场景的理解能力。以下是针对链表相关的常见算法和数据结构问题及其解决方案。 #### 常见链表操作 链表的操作主要包括以下几个方面: - **单向链表反转** 反转一个单向链表是一个经典的题目,在实际应用中有广泛用途。可以通过迭代的方式实现该功能[^1]。 ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def reverse_list(head: ListNode) -> ListNode: prev = None current = head while current is not None: next_node = current.next current.next = prev prev = current current = next_node return prev ``` - **查找中间节点** 使用快慢指针的方法可以在一次遍历中找到链表的中间节点[^2]。 ```python def find_middle_node(head: ListNode) -> ListNode: slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next return slow ``` - **删除倒数第N个节点** 删除链表中的倒数第 N 个节点通常需要用到双指针技巧来定位目标位置。 ```python def remove_nth_from_end(head: ListNode, n: int) -> ListNode: dummy = ListNode(0) dummy.next = head first = second = dummy for _ in range(n + 1): first = first.next while first: first = first.next second = second.next second.next = second.next.next return dummy.next ``` #### LRU缓存机制与链表的关系 LRU(Least Recently Used)是一种常见的缓存淘汰策略,其实现往往依赖于双向链表和哈希表相结合的设计思路。通过维护一个按访问顺序排列的双向链表,可以快速更新最近使用的元素并移除最久未使用的项。 ```python from collections import OrderedDict class LRUCache: def __init__(self, capacity: int): self.cache = OrderedDict() self.capacity = capacity def get(self, key: int) -> int: if key not in self.cache: return -1 self.cache.move_to_end(key) return self.cache[key] def put(self, key: int, value: int) -> None: if key in self.cache: self.cache.move_to_end(key) self.cache[key] = value if len(self.cache) > self.capacity: self.cache.popitem(last=False) ``` 上述代码展示了如何利用 `OrderedDict` 来模拟 LRU 缓存的行为,其中涉及到了链表的相关概念和技术细节。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值