目录
顺序表和链表
1.线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
2.顺序表
2.1概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
1. 静态顺序表:使用定长数组存储元素
2. 动态顺序表:使用动态开辟的数组存储
2.2 接口实现
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,实现动态顺序表:
typedef int SLDATATYPE;
typedef struct SeqList
{
SLDATATYPE* a;
size_t size;
size_t capacity;
}SqList;
//初始化
void SqListInit(SqList* ps);
//尾插
void SqListPushBack(SqList* ps);
//头插
void SqListPushFront(SqList* ps, SLDATATYPE x);
//任意位置插入
void SqListInsert(SqList* ps, size_t pos, SLDATATYPE x);
//尾删
void SqListPopBack(SqList* ps);
//头删
void SqListPopFront(SqList* ps);
//任意位置删除
void SqListErase(SqList* ps, size_t pos);
//销毁
void SqListDestory(SqList* ps);
//打印
void SqListPrint(SqList* ps);
//检查容量是否充足
void CheckSqList(SqList* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
SLDATATYPE* tmp = (SLDATATYPE*)realloc(ps->a, sizeof(SLDATATYPE) * ps->capacity * 2);
if (tmp == NULL)
{
perror(CheckSqList);
exit(-1);
}
ps->a = tmp;
ps->capacity *= 2;
}
}
//初始化
void SqListInit(SqList* ps)
{
ps->a = (SLDATATYPE*)malloc(sizeof(SLDATATYPE) * 4);
if (ps->a == NULL)
{
perror("SqListInit");
exit(-1);
}
ps->size = 0;
ps->capacity = 4;
}
//任意位置插入
void SqListInsert(SqList* p, size_t pos, SLDATATYPE x)
{
assert(p);
CheckSqList(p);
if (p->size == 0)
assert(pos < p->size + 1);
else
assert(pos < p->size);
int end = (int)p->size - 1;
for (; end >= (int)pos; end--)
{
p->a[end+1] = p->a[end];
}
p->a[pos] = x;
p->size++;
}
//头插
void SqListPushFront(SqList* ps, SLDATATYPE x)
{
SqListInsert(ps, 0, x);
}
//尾插
void SqListPushBack(SqList* ps, SLDATATYPE x)
{
SqListInsert(ps, ps->size, x);
}
//任意位置删除
void SqListErase(SqList* ps, size_t pos)
{
assert(ps);
if (ps->size == 0)
{
printf("No Data to Delet\n");
return;
}
assert(pos < ps->size);
size_t end = (int)pos;
while (end < ps->size - 1)
{
ps->a[end] = ps->a[end + 1];
end++;
}
ps->size--;
}
//头删
void SqListPopFront(SqList* ps)
{
SqListErase(ps, 0);
}
//尾删
void SqListPopBack(SqList* ps)
{
SqListErase(ps, ps->size - 1);
}
//销毁
void SqListDestory(SqList* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = 0;
ps->size = 0;
}
void SqListPrint(SqList* ps)
{
assert(ps);
for (size_t i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
2.3 顺序表相关练习
解决该题需要用到两个指针(下标):str1与str2,当str1与str2同时向后遍历数组,当nums[str2] == val时,str2++,当nums[str2 != val时,str2++。这种解法时间复杂度为O(n),空间复杂度为O(1)
int removeElement(int* nums, int numsSize, int val)
{
int str1=0;
int str2=0;
while(str1<numsSize)
{
if(nums[str1]!=val)
{
nums[str2++]=nums[str1++];
}
else
{
str1++;
}
}
return str2;
}
26. 删除有序数组中的重复项 - 力扣(LeetCode)
解决该题需要用到两个指针(下标):str1与str2,当str1与str2同时向后遍历数组,当nums[str1] != nums[str2]时,str1++,当nums[str1] == nums[str2]时,让nums[str2] = nums[str1],同时用k记录此时数组的长度。这种解法时间复杂度为O(n),空间复杂度为O(1)
int removeDuplicates(int* nums, int numsSize)
{
int str1=1;
int str2=1;
int k =1;
while(str1<numsSize)
{
if(nums[str1]!=nums[str1-1])
{
nums[str2++]=nums[str1++];
k++;
}
else
{
str1++;
}
}
return k;
}
解决该题需要用到三个指针(下标):end1、end2和end,end、end1与end2同时从后往前分别遍历数组num1(end = nums1Size-1,end1 = m-1)与num2(下标为n-1),比较nums1[end1]与nums2[end2]的大小,当nums1[end1] > nums2[end2]时,让nums1[end]=nums1[end1],当nums1[end1] < nums2[end2]时,让nums1[end]=nums2[end2],遍历后,若num2未遍历完,则将剩余的执行操作nums1[end]=nums2[end2]。这种解法时间复杂度为O(n),空间复杂度为O(1)
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
int end1 = m-1;
int end2 = n-1;
int end = nums1Size-1;
while(end>=0 && end1>=0 && end2>=0)
{
if(nums1[end1]>nums2[end2])
{
nums1[end--]=nums1[end1--];
}
else
{
nums1[end--]=nums2[end2--];
}
}
while(end2>=0 && end>=0)
{
nums1[end--]=nums2[end2--];
}
}
2.4 顺序表的问题及思考
1. 中间/头部的插入删除,时间复杂度为O(N)
2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗
3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到
200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
链表可以很好的解决以上问题。
3.链表
3.1 链表的概念及结构
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的
3.2 链表的分类
链表的结构非常多样,以下情况组合起来就有8种链表结构:
1. 单向或者双向
2. 带头或者不带头
3. 循环或者非循环
实际中最常用的两种结构:
1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。
2.带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表,此结构虽然结构复杂,但是使用代码实现后结构会带来很多优势,实现反而简单
3.3 链表的实现
创建节点,增删查改
typedef int SLNodeDataType;
typedef struct SLNode
{
SLNodeDataType num;
struct SLNode* next;
}SLNode;
//增删查改
//创建节点
SLNode* BuySLNode(SLNodeDataType x);
//头插
void SLPushFront(SLNode** phead, SLNodeDataType x);
//尾插
void SLPushBack(SLNode** pphead, SLNodeDataType x);
//头删
void SLPopFront(SLNode** pphead);
//尾删
void SLPopBack(SLNode** pphead);
//查找
SLNode* SLFind(SLNode* phead, SLNodeDataType x);
//pos位置删除
void SLErase(SLNode** pphead, SLNode* pos);
//pos位置后删除
void SLEraseAfter(SLNode** pphead, SLNode* pos);
//pos位置之前增加
void SLInsert(SLNode** pphead, SLNode* pos, SLNodeDataType x);
//pos位置之后增加
void SLInsertAfter(SLNode* pos, SLNodeDataType x);
void SLDestroy(SLNode** pphead);
//打印
void SLPrint(SLNode* phead);
SLNode* BuySLNode(SLNodeDataType x)
{
SLNode* phead = (SLNode*)malloc(sizeof(SLNode));
phead->num = x;
phead->next = NULL;
return phead;
}
//头插
void SLPushFront(SLNode** pphead, SLNodeDataType x)
{
if (*pphead == NULL)
{
*pphead = BuySLNode(x);
}
else
{
SLNode* tail = BuySLNode(x);
tail->next = *pphead;
*pphead = tail;
}
}
//尾插
void SLPushBack(SLNode** pphead, SLNodeDataType x)
{
if (*pphead == NULL)
{
*pphead = BuySLNode(x);
}
else
{
SLNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = BuySLNode(x);
}
}
//头删
void SLPopFront(SLNode** pphead)
{
assert(*pphead);
SLNode* tail = *pphead;
*pphead = tail->next;
free(tail);
}
//尾删
void SLPopBack(SLNode** pphead)
{
assert(*pphead);
SLNode* tail = *pphead;
if (tail->next == NULL)
{
free(tail);
*pphead = NULL;
}
else
{
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
//查找
SLNode* SLFind(SLNode* phead, SLNodeDataType x)
{
SLNode* tail = phead;
while (tail)
{
if (tail->num == x)
{
return tail;
}
tail = tail->next;
}
return tail;
}
//pos位置删除
void SLErase(SLNode** pphead, SLNode* pos)
{
assert(pphead);
assert(*pphead);
assert(pos);
SLNode* tail = *pphead;
if (pos == *pphead)
{
SLPopFront(pphead);
return;
}
while (tail)
{
if (tail->next == pos)
{
SLNode* cur = tail->next;
tail->next = cur->next;
free(cur);
return;
}
else
{
tail = tail->next;
}
}
}
//pos位置后删除
void SLEraseAfter(SLNode** pphead, SLNode* pos)
{
SLErase(pphead, pos->next);
}
//pos位置之前增加
void SLInsert(SLNode** pphead, SLNode* pos, SLNodeDataType x)
{
assert(pphead);
assert(*pphead);
assert(pos);
SLNode* newnode = BuySLNode(x);
SLNode* tail = *pphead;
if (pos == *pphead)
{
newnode->next = tail;
*pphead = newnode;
return;
}
while (tail->next != pos)
{
tail = tail->next;
}
newnode->next = tail->next;
tail->next = newnode;
}
//pos位置之后增加
void SLInsertAfter(SLNode* pos, SLNodeDataType x)
{
assert(pos);
SLNode* newnode = BuySLNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
//销毁
void SLDestroy(SLNode** pphead)
{
assert(pphead);
assert(*pphead);
SLNode* cur = *pphead;
SLNode* next = NULL;
while (cur)
{
next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
void SLPrint(SLNode* phead)
{
SLNode* tail = phead;
while (tail)
{
printf("%d->", tail->num);
tail = tail->next;
}
printf("NULL\n");
}
3.4 链表练习
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* prev = NULL;
struct ListNode* cur = head;
while(cur)
{
if(cur->val!=val)
{
prev = cur;
cur = cur->next;
}
else
{
if(prev == NULL)
{
prev = cur;
cur=cur->next;
head = cur;
free(prev);
prev = NULL;
}
else
{
prev->next = cur->next;
free(cur);
cur = prev->next;
}
}
}
return head;
}
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* rhead = NULL;
struct ListNode* cur = head;
while(cur)
{
struct ListNode* next = cur->next;
cur->next = rhead;
rhead = cur;
cur = next;
}
return rhead;
}
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head)
{
struct ListNode* slow = head,* fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
struct ListNode* head = NULL;
struct ListNode* tail = NULL;
if(list1==NULL)
return list2;
if(list2==NULL)
return list1;
while(list1 && list2)
{
if(list1->val < list2->val)
{
if(tail == NULL)
{
tail = head = list1;
}
else
{
tail->next = list1;
tail = tail->next;
}
list1 = list1->next;
}
else
{
if(tail == NULL)
{
tail = head = list2;
}
else
{
tail->next = list2;
tail = tail->next;
}
list2 = list2->next;
}
}
if(list1)
tail->next = list1;
if(list2)
tail->next = list2;
return head;
}
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:
ListNode* partition(ListNode* pHead, int x)
{
// write code here
if(pHead == NULL)
{
return NULL;
}
struct ListNode* lessNode = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* GreaterNode = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* lesstail = lessNode;
struct ListNode* Greatertail = GreaterNode;
struct ListNode* cur = pHead;
while(cur)
{
if(cur->val < x)
{
lesstail->next = cur;
lesstail = lesstail->next;
}
else
{
Greatertail->next = cur;
Greatertail = Greatertail->next;
}
cur = cur->next;
}
lesstail->next = GreaterNode->next;
Greatertail->next = NULL;
pHead = lessNode->next;
free(lessNode);
free(GreaterNode);
return pHead;
}
};
链表的回文结构_牛客题霸_牛客网 (nowcoder.com)
struct ListNode* middleNode(struct ListNode* head)
{
struct ListNode* slow = head,* fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* rhead = NULL;
struct ListNode* cur = head;
while(cur)
{
struct ListNode* next = cur->next;
cur->next = rhead;
rhead = cur;
cur = next;
}
return rhead;
}
bool chkPalindrome(ListNode* phead)
{
// write code here
ListNode* mid = middleNode(phead);
ListNode* rmid = reverseList(mid);
ListNode* tail = phead;
ListNode* rmidtail = rmid;
while(rmidtail)
{
if(tail->val != rmidtail->val)
return false;
tail = tail->next;
rmidtail = rmidtail->next;
}
return true;
}
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
struct ListNode *Atail = headA;
struct ListNode *Btail = headB;
int lenA = 1;
int lenB = 1;
while(Atail->next)
{
Atail = Atail->next;
lenA++;
}
while(Btail->next)
{
Btail = Btail->next;
lenB++;
}
if(Atail!=Btail)
{
return NULL;
}
else
{
int gap = abs(lenA-lenB);
struct ListNode* lessNode= headA;
struct ListNode* greaterNode= headB;
if(lenA>lenB)
{
greaterNode = headA;
lessNode = headB;
}
struct ListNode* lesstail= lessNode;
struct ListNode* greatertail= greaterNode;
while(gap--)
{
greatertail = greatertail->next;
}
while(greatertail != lesstail)
{
lesstail = lesstail->next;
greatertail = greatertail->next;
}
return lesstail;
}
}
/**
* 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)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
return true;
}
return false;
}
/**
* 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;
struct ListNode* pos = NULL;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
{
pos = fast;
break;
}
}
if(!pos)
{
return NULL;
}
struct ListNode* tail = head;
while(tail != pos)
{
tail = tail->next;
pos = pos->next;
}
return pos;
}
/**
* Definition for a Node.
* struct Node {
* int val;
* struct Node *next;
* struct Node *random;
* };
*/
struct Node* copyRandomList(struct Node* phead)
{
struct Node* cur = phead;
struct Node* copy = NULL;
while(cur)
{
struct Node* curnext = cur->next;
copy = (struct Node*)malloc(sizeof(struct Node));
copy->val = cur->val;
copy->next = curnext;
cur->next = copy;
cur = curnext;
}
cur = phead;
while(cur)
{
copy = cur->next;
if(cur->random == NULL)
{
copy->random = NULL;
}
else
{
copy->random = cur->random->next;
}
cur = copy->next;
}
struct Node* copyphead = NULL;
struct Node* copytail = NULL;
cur = phead;
while(cur)
{
copy = cur->next;
if(copyphead == NULL)//将新链表整理出来
{
copyphead = copytail = copy;
}
else
{
copytail->next = copy;
copytail = copytail->next;
}
cur->next = copy->next;//恢复phead
cur = cur->next;
}
return copyphead;
}
3.5 双向链表的实现
typedef int LTDataType;
typedef struct ListNode
{
LTDataType data;
struct ListNode* prev;
struct ListNode* next;
}ListNode;
//创建头结点
ListNode* ListCreate();
//头插
void ListPushFront(ListNode* plist, LTDataType data);
//头删
void ListPopFront(ListNode* phead);
//尾插
void ListPushBack(ListNode* phead, LTDataType data);
//尾删
void ListPopBack(ListNode* phead);
//查找
ListNode* ListFind(ListNode* phead, LTDataType x);
//pos之前插入
void ListInsert(ListNode* pos, LTDataType x);
//pos删除
void ListErase(ListNode* pos);
//销毁链表
void ListDestory(ListNode* phead);
//判断链表是否为空
bool LNodeEmpty(ListNode* phead);
//打印
void ListPrint(ListNode* phead);
//新增节点
ListNode* BuyNewNode(LTDataType data)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
perror("BuyNewNode fail");
printf("Great newnode fail");
return;
}
newnode->data = data;
newnode->next = NULL;
newnode->prev = NULL;
}
//判断链表是否为空
bool LNodeEmpty(ListNode* phead)
{
assert(phead);
return phead->next == phead;
}
// 创建返回链表的头结点.
ListNode* ListCreate()
{
ListNode* head = (ListNode*)malloc(sizeof(ListNode));
head->next = head;
head->prev = head;
return head;
}
// 双向链表销毁
void ListDestory(ListNode* phead)
{
ListNode* cur = phead->next;
while (cur != phead)
{
ListNode* curnext = cur->next;
free(cur);
cur = curnext;
}
free(phead);
}
// 双向链表打印
void ListPrint(ListNode* phead)
{
ListNode* cur = phead->next;
while (cur != phead)
{
printf("%d<=>", cur->data);
cur = cur->next;
}
printf("\n");
}
// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType data)
{
assert(phead);
ListNode* newnode = BuyNewNode(data);
ListNode* tail = phead->prev;
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
// 双向链表尾删
void ListPopBack(ListNode* phead)
{
assert(phead);
assert(!LNodeEmpty(phead));
ListNode* tail = phead->prev;
ListNode* tailprev = tail->prev;
phead->prev = tailprev;
tailprev->next = phead;
free(tail);
}
//双向链表头插
void ListPushFront(ListNode* phead, LTDataType data)
{
ListNode* newnode = BuyNewNode(data);
newnode->next = phead->next;
newnode->next->prev = newnode;
newnode->prev = phead;
phead->next = newnode;
}
//双向链表头删
void ListPopFront(ListNode* phead)
{
assert(phead);
assert(!LNodeEmpty(phead));
ListNode* tail = phead->next;
ListNode* tailNext = tail->next;
phead->next = tailNext;
tailNext->prev = phead;
free(tail);
}
//双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x)
{
ListNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
printf("未找到\n");
return NULL;
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* prev = pos->prev;
ListNode* newnode = BuyNewNode(x);
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
assert(pos);
if (pos->next != pos)
{
ListNode* posPrev = pos->prev;
ListNode* posNext = pos->next;
posPrev->next = posNext;
posNext->prev = posPrev;
free(pos);
}
else
{
printf("删除失败,链表无数据\n");
}
}
4.顺序表和链表的区别和联系
不同点 | 顺序表 | 链表 |
存储空间上 | 物理上一定连续 | 逻辑上连续,但物理上不一定 连续 |
随机访问 | 支持O(1) | 不支持:O(N) |
任意位置插入或者删除元素 | 可能需要搬移元素,效率低O(N) | 只需修改指针指向 |
插入 | 动态顺序表,空间不够时需要扩容 | 没有容量的概念 |
应用场景 | 元素高效存储+频繁访问 | 任意位置插入和删除频繁 |
缓存利用率 | 高 | 低 |