提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、对比
1.顺序表:
1.开辟方式
通过在堆上开辟空间,为什么要在堆上开辟空间呢?因为,出了函数数据不销毁的有三种:全局变量,静态变量,和在堆上开辟的空间,因为在堆上开辟的空间,如果没有free掉,出了函数的作用域还是会存在,所以只要返回一个头指针就可以管理起来,很方便。
当然也有静态的,就是一开始就把空间给固定死的那种,不过呢,这种很不方便,比如,我们开了1000个数据的空位,但是用户只需要存一个数据,这就会造成空间的浪费当然还有就是空间不够的画就会很难受,因为空间的大小被固定死了。2.优势:
访问速度快,可以直接通过下标进行数据的访问,尾插效率高。
3.劣势:
因为当空问不够时,我们是按照原空问的两倍进行realloc所以也会造成空间上的一定浪费,而每一次的realloc数据批会新进行拷贝一份,所以效率不高。
在头插或者是某个位置插入,数据都会移动(假设数据长度是n,要插在下标为i的位置移动)n-i次.
在删除中间的某个数据时,数据会移动n-i-1个数据,效率也很低。
2.单链表:
在物理空间上不一定连续,因为是通过malloc出来的,地址是随机的。
在逻辑上一定是连续的。
优势:
头插,尾插,在pos位置插入数据,头删,效率都很高,内存的管理非常好,需要多少开辟多少不会造成内存的浪费。
劣势:
对于尾删,删除在pos位置的数据的效率很低,因为不仅需要找到他们的对应位置,还需要找到对应位置的前一个,但是对于单链表来说找到对应位置的前一个就需要再遍历一遍,所以效率是比较低的。
所以,单链表虽然解决了,空间的浪费,但是还是有着一定的缺陷,所以就出现双向带头链表。
结构如下,虽然看起来比较复杂,但是很好用。
可以看见,双向带头链表直接解决了单链表中,尾删,在pos位置删除需要走两遍的问题,因为他们每一个数据都存有上一个数据的地址,这样就不需要走两遍了,而且头删,头插,在pos位置插入的效率同样是很高的。
二、经典例题
88. 合并两个有序数组 - 力扣(Leetcode)https://leetcode.cn/problems/merge-sorted-array/
代码如下:
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) { int p=m+n-1;//nums1的最右边的下标 int p1=m-1;//nums1中需要比较的数据 int p2=n-1;//nums2右端下标 while(p1!=-1&&p2!=-1) { if(nums1[p1]>nums2[p2]) { nums1[p]=nums1[p1]; p--; p1--; } else { nums1[p]=nums2[p2]; p--; p2--; } } while(p2!=-1) { nums1[p]=nums2[p2]; p--; p2--; } }
876. 链表的中间结点 - 力扣(Leetcode)https://leetcode.cn/problems/middle-of-the-linked-list/description/
代码如下:
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* middleNode(struct ListNode* head) { //快指针一次走两步,慢指针一次走一步 struct ListNode* fast=head; struct ListNode* slow=head; while(fast&&fast->next) { fast=fast->next->next; slow=slow->next; } return slow; }
160. 相交链表 - 力扣(Leetcode)https://leetcode.cn/problems/intersection-of-two-linked-lists/description/
代码如下:
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) { struct ListNode *p1=headA; struct ListNode *p2=headB; while(p1!=p2) { p1=(p1==NULL? headB:p1->next); p2=(p2==NULL? headA:p2->next); } return p1; }
141. 环形链表 - 力扣(Leetcode)https://leetcode.cn/problems/linked-list-cycle/description/
理解如下:
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head)
{
struct ListNode *slow=head;
struct ListNode *fast=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
return true;
}
}
return false;
}
142. 环形链表 II - 力扣(Leetcode)https://leetcode.cn/problems/linked-list-cycle-ii/description/
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode *slow=head;
struct ListNode *fast=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
struct ListNode *meet=fast;
struct ListNode *cur=head;
while(meet!=cur)
{
cur=cur->next;
meet=meet->next;
}
return cur;
}
}
return NULL;
}