知识体
1.struct定义
struct S{
int a;
char b;
float c;
}s1;
通常是struct 结构体名称 { 变量 } 结构体变量 ;
假如出现
struct {
int a;
char b;
float c;
}S;
这种情况其实也是定义为S结构体
2.typedef使用
typedef是用来重新定义一个数据类型为另一个名称(不止可以用于结构体):
typedef struct s1{
~~~
}TT;
这个实际上是将struct s1别名为TT;
在C++中无需这样操作,直接s1 变量名 ,就能定义一个这种结构体变量。
而在C中则需要利用typedef为struct s1取别名为TT,也就是"TT" = "struct s1",效果相同。(即struct s1 变量名 等效于 TT s变量名)
3.union联合体
存在的意义:结构体所占内存即所有类型所占字节总和,但有时候我们只想利用其中单个变量,对于其他变量则在不需要调用。如果用结构体定义一个变量组,就可能会造成不必要的资源浪费。
为了使这种需求实现且减少资源浪费,利用union联合体即可。
union用法:定义与结构体类似
union S{
int a;
char b;
float c;
}s1;
这个union联合体所占内存即最大变量所占内存,即所有变量共用同一个内存
char
通常占 1 个字节。int
的大小通常为 4 个字节(32 位系统)或 8 个字节(64 位系统)。float
通常占 4 个字节。
即S所占内存为4字节;
****注意!!:
在union联合体中进行赋值操作时会对这个内存进行赋值操作
这里将s1.a赋值后,即对整个s1所对应的值进行了赋值,字符类变量b转换成为对应的ascll值。
即整体赋值。
4.链表
链表与数组都是基本的数据结构,根本区别在于调用内存的方式。
前者为:动态分配 后者为:手动分配
1.对比
数组的特点:
- 在内存中,数组是一块连续的区域。
- 数组需要预留空间,在使用前需要提前申请所占内存的大小,这样不知道需要多大的空间,就预先申请可能会浪费内存空间,即数组空间利用率低。
- 在数组起始位置处,插入数据和删除数据效率低。插入数据时,待插入位置的的元素和它后面的所有元素都需要向后搬移。删除数据时,待删除位置后面的所有元素都需要向前搬移。
- 随机访问效率很高,时间复杂度可以达到O(1)。因为数组的内存是连续的,想要访问那个元素,直接从数组的首地址处向后偏移就可以访问到了。
- 数组开辟的空间,在不够使用的时候需要扩容,扩容的话,就会涉及到需要把旧数组中的所有元素向新数组中搬移。
- 数组的空间是从栈分配的。
链表的特点:
- 在内存中,元素的空间可以在任意地方,空间是分散的,不需要连续。
- 链表中的元素都会两个属性,一个是元素的值,另一个是指针,此指针标记了下一个元素的地址。每一个数据都会保存下一个数据的内存的地址,通过此地址可以找到下一个数据。
- 查找数据时效率低,时间复杂度为O(N)。因为链表的空间是分散的,所以不具有随机访问性,如要需要访问某个位置的数据,需要从第一个数据开始找起,依次往后遍历,直到找到待查询的位置,故可能在查找某个元素时,时间复杂度达到O(N)。
- 空间不需要提前指定大小,是动态申请的,根据需求动态的申请和删除内存空间,扩展方便,故空间的利用率较高。
- 任意位置插入元素和删除元素效率较高,时间复杂度为O(1)。
- 链表的空间是从堆中分配的。
总结:
- 对于想要快速访问数据,不经常有插入和删除元素的时候,选择数组。
- 对于需要经常的插入和删除元素,而对访问元素时的效率没有很高要求的话,选择链表。
以上内容以New Bing总结于数组和链表的区别_链表和数组的区别-CSDN博客面试题解答系列:数组和链表的区别 - 知乎
2.定义
链表定义是以结构体为基础创建的通常为
struct ListNode {
int data; // 数据域
ListNode *next; // 指向下一个节点的指针
};
这个结构体要有数据域与指向下个节点的指针
静态链表:
struct ListNode s1, s2, s3, *head, *p;
head = &s1;
scanf("%d,%d,%d", &s1.data, &s2.data, &s3.data);
s1.next = &s2;
s2.next = &s3;
s3.next = NULL;
p = head;
do {
printf("%d", p->data);
p = p->next;
} while (p != NULL);
动态链表:(两数之和-Leetcode)
typedef struct {
int val;//值
ListNode *next;//指向下个节点的指针
}ListNode;
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *head=nullptr,*tail=nullptr;
int carry=0;
while(l1||l2) //循环直到l1与l2都为空指针
{
int n1=l1?l1->val:0; //不为空指针即执行赋值为l1中的值
int n2=l2?l2->val:0;
int sum=n1+n2+carry;
if(!head){//head 为空指针执行
head=tail=new ListNode(sum%10);
}
else{
tail->next=new ListNode(sum%10);
tail=tail->next;
}
carry=sum/10;
if(l1){
l1=l1->next;
}
if(l2){
l2=l2->next;
}
}
if(carry>0){
tail->next=new ListNode(carry);
}
return head;
}
流程:
首先,定义了一个名为
ListNode
的结构体,它有一个整数val
和一个指向下一个ListNode
的指针next
。
addTwoNumbers
函数接收两个ListNode
指针l1
和l2
作为参数,这两个指针分别指向两个链表的头部。在函数内部,定义了两个
ListNode
指针head
和tail
,以及一个名为carry
的整数,用于存储进位。然后,进入一个循环,只要
l1
或l2
中有一个不是空指针,就会继续循环。在每次循环中,首先计算l1
和l2
当前节点的值(如果对应的指针不是空的话)以及进位carry
的和。如果
head
是空指针,那么就创建一个新的ListNode
,其值为sum
对 10 取余的结果,并将head
和tail
都指向这个新节点。如果
head
不是空指针,那么在tail
后面添加一个新的ListNode
,其值也是sum
对 10 取余的结果,并更新tail
为新添加的节点。更新
carry
为sum
除以 10 的结果。如果
l1
或l2
不是空指针,那么将它们更新为指向下一个节点。在循环结束后,如果
carry
大于 0,那么在tail
后面添加一个新的ListNode
,其值为carry
。最后,函数返回
head
,即结果链表的头部。
- 尾部指针在每次更新值的时候就已经将上个节点与下一个节点连接在一起了,这种连接关系并不会随着尾部指针的更新而破坏而是继续更新
- 详细讲解:C语言链表超详解https://blog.csdn.net/k666499436/article/details/124787990