目录
线性表
线性表:是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构。线性表是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表,链表,栈,队列,字符串......
顺序表
概念
顺序表:顺序表是用一段物理地址连续的存储单元存储数据结构元素的线性结构,一般用数组存储,在数组上完成增删查改。
静态顺序表:使用定长数组存储元素
动态顺序表:使用动态开辟的数组存储元素
静态顺序表的定义:(空间开多了浪费,开少了不够用)
// 顺序表的静态存储
#include <stdio.h>
#define N 7 // 静态表的大小
typedef int SLDataType; // 静态表的数据类型
typedef struct SeqList
{
SLDataType arr[N]; // 定长数组
size_t size; // 当前已经存储的个数
}SeqList;
动态顺序表的定义:
typedef int SLDataType; // 存储数据的类型
typedef struct SeqList
{
SeqList* a; // 指向存储数据的数组的地址,存储的数据用动态开辟
int size; // 表示当前已经存在的个数
int capacity; // 表示容量
}SeqList;
接口的实现:
初始化
void SeqListInit(SeqList* sl); // 初始化顺序表
void SeqListInit(SeqList* sl)
{
assert(sl);
sl->sl = NULL;
sl->size = 0;
sl->capacity = 0;
}
检查空间
void SeqListCheckCapacity(SeqList* sl); // 检查容量是否够,不够扩容
void SeqListCheckCapacity(SeqList* sl)
{
assert(sl);
if (sl->size == sl->capacity) {
size_t newCapacity = sl->capacity == 0 ? 4 : sl->capacity * 2;
SLDataType* tmp = realloc(sl->sl, sizeof(SeqList) * newCapacity);
if (tmp == NULL) {
printf("realloc fail\n");
exit(-1);
} else {
sl->sl = tmp;
sl->capacity = newCapacity;
}
}
}
顺序表尾插
void SeqListPushBack(SeqList* sl, SLDataType x); // 尾插
void SeqListPushBack(SeqList* sl, SLDataType x)
{
assert(sl);
SeqListCheckCapacity(sl);
sl->sl[sl->size] = x;
sl->size++;
}
void SeqListPushBack(SeqList* sl, SLDataType x)
{
assert(sl);
SeqListInsert(sl, sl->size, x);
}
顺序表头插
void SeqListPushFront(SeqList* sl, SLDataType x); // 头插
void SeqListPushFront(SeqList* sl, SLDataType x)
{
assert(sl);
SeqListCheckCapacity(sl);
int end = sl->size - 1;
while (end >= 0) {
sl->sl[end + 1] = sl->sl[end];
end--;
}
sl->sl[0] = x;
sl->size++;
}
void SeqListPushFront(SeqList* sl, SLDataType x)
{
assert(sl);
SeqListInsert(sl, 0, x);
}
顺序表尾删
void SeqListPopBack(SeqList* sl); // 尾删
void SeqListPopBack(SeqList* sl)
{
assert(sl);
if (sl->size > 0) {
sl->size--;
}
}
void SeqListPopBack(SeqList* sl)
{
assert(sl);
SeqListErase(sl, sl->size - 1);
}
顺序表头删
void SeqListPopFront(SeqList* sl); // 头删
void SeqListPopFront(SeqList* sl)
{
assert(sl);
if (sl->size > 0) {
int begin = 1;
while (begin < sl->size) {
sl->sl[begin - 1] = sl->sl[begin];
++begin;
}
sl->size--;
}
}
void SeqListPopFront(SeqList* sl)
{
assert(sl);
SeqListErase(sl, 0);
}
顺序表查找
void SeqListFind(SeqList* sl, SLDataType x); // 根据x值找到x的下标
void SeqListFind(SeqList* sl, SLDataType x)
{
assert(sl);
for (int i = 0; i < sl->size; i++) {
if (sl->sl[i] == x) return i;
}
return -1;
}
顺序表在pos位置插入x
void SeqListInsert(SeqList* sl, size_t pos, SLDataType x); // 在pos位置插入x
void SeqListInsert(SeqList* sl, size_t pos, SLDataType x)
{
assert(sl);
assert(pos <= sl->size);
SeqListCheckCapacity(sl);
size_t end = sl->size;
// 往任意位置插入数据时,可以用前一个值将这个值覆盖
// 可以把这个值放在后面的那个位置。
// 如果是第二种的时候,end = sl->size - 1,判断条件end >= pos
// 当没有元素时,就是插入第一个元素,因为end是无符号类型,end - 1是无穷大,将会永远为真
// 处理方案是将pos强制转换为int类型。或者使用第一种
while (end > pos) {
sl->sl[end] = sl->sl[end - 1];
end--;
}
sl->sl[pos] = x;
sl->size++;
}
顺序表删除pos位置
void SeqListErase(SeqList* sl, size_t pos); // 删除pos位置的信息
void SeqListErase(SeqList* sl, size_t pos)
{
assert(sl);
assert(pos < sl->size);
size_t begin = pos + 1;
while (begin < sl->size) {
sl->sl[begin - 1] = sl->sl[begin];
begin++;
}
sl->size--;
}
顺序表销毁
void SeqListDestroy(SeqList* sl); // 删除顺序表
void SeqListDestroy(SeqList* sl)
{
assert(sl);
sl->sl = NULL;
sl->size = 0;
sl->capacity = 0;
}
顺序表打印
void SeqListPrint(SeqList* sl); // 打印列表
void SeqListPrint(SeqList* sl)
{
assert(sl);
for (int i = 0; i < sl->size; i++) {
printf("%d ", sl->sl[i]);
}
printf("\n");
}
链表
概念:链表是一种物理存储结构上非连续性、非顺序的存储结构,数据元素的逻辑是通过链表中的指针链接次序实现的。
链表的分类:单向、双向、带头、不带头、循环、非循环、无头单向循环、带头双向循环
单链表:
链表的定义:
typedef int SLDataType;
typedef struct SListNode
{
SLDataType val;
struct SListNode* next;
}SListNode;
实现:
链表的打印
void SListPrint(SListNode* phead);
void SListPrint(SListNode* phead)
{
SListNode* cur = phead;
while (cur != NULL) {
printf("%d->", cur->val);
cur = cur->next;
}
printf("NULL\n");
}
链表的尾插
void SListPushBack(SListNode** pphead, SLDataType x);
SListNode* BuySListNode(SLDataType x)
{
SListNode* NewNode = (SListNode*)malloc(sizeof(SListNode));
if (NewNode == NULL) {
printf("malloc failed\n");
exit(-1);
} else {
NewNode->val = x;
NewNode->next = NULL;
}
return NewNode;
}
void SListPushBack(SListNode** pphead, SLDataType x)
{
assert(pphead);
SListNode* NewNode = BuySListNode(x);
if (*pphead == NULL) {
*pphead = NewNode;
} else {
SListNode* tail = *pphead;
while (tail->next != NULL) {
tail = tail->next;
}
tail->next = NewNode;
}
}
链表的头插
void SListPushFront(SListNode** pphead, SLDataType x);
SListNode* BuySListNode(SLDataType x)
{
SListNode* NewNode = (SListNode*)malloc(sizeof(SListNode));
if (NewNode == NULL) {
printf("malloc failed\n");
exit(-1);
} else {
NewNode->val = x;
NewNode->next = NULL;
}
return NewNode;
}
void SListPushFront(SListNode** pphead, SLDataType x)
{
assert(pphead);
SListNode* NewNode = BuySListNode(x);
NewNode->next = *pphead;
*pphead = NewNode;
}
链表的尾删
void SListPopBack(SListNode** pphead);
void SListPopBack(SListNode** pphead)
{
assert(pphead);
if (*pphead == NULL) {
return;
} else if ((*pphead)->next == NULL) {
free(*pphead);
*pphead = NULL;
} else {
SListNode* tail = *pphead;
while (tail->next->next) {
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
链表的头删
void SListPopFront(SListNode** pphead);
void SListPopFront(SListNode** pphead)
{
assert(pphead);
if (*pphead == NULL) {
return;
} else {
SListNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
}
链表的查找
SListNode* SListFind(SListNode* phead, SLDataType x);
SListNode* SListFind(SListNode* phead, SLDataType x)
{
SListNode* cur = phead;
while (cur) {
if (cur->val == x) {
return cur;
}
cur = cur->next;
}
return NULL;
}
在pos位置之前插入
void SListInsertBefore(SListNode** pphead, SListNode* pos, SLDataType x);
SListNode* BuySListNode(SLDataType x)
{
SListNode* NewNode = (SListNode*)malloc(sizeof(SListNode));
if (NewNode == NULL) {
printf("malloc failed\n");
exit(-1);
} else {
NewNode->val = x;
NewNode->next = NULL;
}
return NewNode;
}
void SListInsertBefore(SListNode** pphead, SListNode* pos, SLDataType x)
{
assert(pphead && pos);
if (pos == *pphead) {
SListPushFront(pphead, x);
} else {
SListNode* pre = *pphead;
while (pre->next != pos) {
pre = pre->next;
}
SListNode* NewNode = BuySListNode(x);
pre->next = NewNode;
NewNode->next = pos;
}
}
在pos位置之后插入
void SListInsertAfter(SListNode* pos, SLDataType x);
SListNode* BuySListNode(SLDataType x)
{
SListNode* NewNode = (SListNode*)malloc(sizeof(SListNode));
if (NewNode == NULL) {
printf("malloc failed\n");
exit(-1);
} else {
NewNode->val = x;
NewNode->next = NULL;
}
return NewNode;
}
void SListInsertAfter(SListNode* pos, SLDataType x)
{
assert(pos);
SListNode* NewNode = BuySListNode(x);
NewNode->next = pos->next;
pos->next = NewNode;
}
删除pos位置
void SListErase(SListNode** pphead, SListNode* pos);
void SListErase(SListNode** pphead, SListNode* pos)
{
assert(pphead && pos);
if (*pphead == pos) {
SListPopFront(pphead);
} else {
SListNode* pre = *pphead;
while (pre->next != pos) {
pre = pre->next;
}
pre->next = pos->next;
free(pos);
pos = NULL;
}
}
删除pos后面的那个位置
void SListEraseAfter(SListNode* pos);
void SListEraseAfter(SListNode* pos)
{
assert(pos);
SListNode* next = pos->next;
if (pos) {
pos->next = next->next;
free(next);
next = NULL;
}
}
销毁列表
void SListDestroy(SListNode** pphead);
void SListDestroy(SListNode** pphead)
{
assert(pphead);
SListNode* cur = *pphead;
while (cur) {
SListNode* next = cur->next;
free(cur);
cur = NULL;
cur = next;
}
*pphead = NULL;
}
双链表
双链表的定义
typedef int LTDataType;
typedef struct ListNode
{
LTDataType val;
struct ListNode* pre;
struct ListNode* next;
}ListNode;
双链表的实现
初始化:
ListNode* ListInit();
ListNode* ListInit()
{
ListNode* phead = BuyListNode(0);
phead->next = phead;
phead->pre = phead;
return phead;
}
尾插:
void ListPushBack(ListNode* phead, LTDataType x);
void ListPushBack(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* tail = phead->pre;
ListNode* newNode = BuyListNode(x);
tail->next = newNode;
newNode->next = phead;
newNode->pre = tail;
phead->pre = newNode;
}
尾删:
void ListPopBack(ListNode* phead);
void ListPopBack(ListNode* phead)
{
assert(phead);
assert(phead->next != phead);
ListNode* tail = phead->pre;
ListNode* tailpre = tail->pre;
free(tail);
tail = NULL;
phead->pre = tailpre;
tailpre->next = phead;
}
头插:
void ListPushFront(ListNode* phead, LTDataType x);
void ListPushFront(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* newNode = BuyListNode(x);
ListNode* front = phead->next;
phead->next = newNode;
newNode->pre = phead;
newNode->next = front;
front->pre = newNode;
}
头删:
void ListPopFront(ListNode* phead);
void ListPopFront(ListNode* phead)
{
assert(phead);
assert(phead->next != phead);
ListNode* front = phead->next;
ListNode* frontnext = front->next;
phead->next = frontnext;
frontnext->pre = phead;
free(front);
front = NULL;
}
打印:
void ListPrint(ListNode* phead);
void ListPrint(ListNode* phead)
{
assert(phead);
ListNode* cur = phead->next;
while (cur != phead) {
printf("%d ", cur->val);
cur = cur->next;
}
printf("\n");
}
查找元素:
ListNode* ListFind(ListNode* phead, LTDataType x);
ListNode* ListFind(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* cur = phead->next;
while (cur != phead) {
if (cur->val == x) {
return cur;
}
cur = cur->next;
}
return NULL;
}
在pos位置之前插入:
void ListInsert(ListNode* pos, LTDataType x);
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* newNode = BuyListNode(x);
ListNode* pre = pos->pre;
pre->next = newNode;
newNode->pre = pre;
newNode->next = pos;
pos->pre = newNode;
}
把pos位置删除:
void ListErase(ListNode* pos);
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* pre = pos->pre;
ListNode* next = pos->next;
pre->next = next;
next->pre = pre;
free(pos);
pos = NULL;
}
销毁链表:
void ListDestory(ListNode* phead);
void ListDestory(ListNode* phead)
{
assert(phead);
ListNode* cur = phead->next;
while (cur) {
ListNode* next = cur->next;
free(cur);
cur = NULL;
}
free(phead);
}
顺序表和链表的区别
不同点 | 顺序表 | 链表 |
存储空间上 | 物理上一定连续 | 逻辑上连续,但物理上不一定连续 |
随机访问 | 支持O(1) | 不支持O(N) |
在任意位置插入或删除元素 | 可能需要搬移元素,效率低O(N) | 只需要修改指针指向 |
插入 | 动态顺序表,空间不够时需要扩容 | 没有容量概念 |
应用场景 | 元素高效存储+频繁访问 | 任意位置插入和删除频繁 |
缓冲利用率 | 高 | 低 |
栈
一种特殊的结构,允许在固定的一端插入和删除元素操作。进行数据插入和删除的一端称为栈顶,另一端称为栈底。遵循先进后出的原则。
栈可以使用数组或链表实现
栈的定义:
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}Stack;
栈的实现:
栈的初始化:
void StackInit(Stack* ps);
void StackInit(Stack* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
压栈:
void StackPush(Stack* ps, STDataType x);
void StackPush(Stack* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity) {
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
ps->a = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
if (ps->a == NULL) {
printf("realloc fail\n");
exit(-1);
}
ps->capacity = newCapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
出栈:
void StackPop(Stack* ps);
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);
(ps->top)--;
}
栈是否为空:
bool StackEmpty(Stack* ps);
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
栈的大小:
int StackSize(Stack* ps);
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
查看栈顶元素:
STDataType StackTop(Stack* ps);
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->a[ps->top - 1];
}
栈的销毁:
void StackDestory(Stack* ps);
void StackDestory(Stack* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
队列
只允许在一端进行插入数据操作,在另一端进行数据删除的特殊线性表,队列具有先进先出的性质。
入队列:进行插入的一端称为队尾
出队列:进行弹出的一端称为队首
队列的定义:
typedef int QDataType;
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QueueNode;
typedef struct Queue
{
QueueNode* head;
QueueNode* tail;
}Queue;
队列的实现:
初始化:
void QueueInit(Queue* pq);
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
}
销毁:
void QueueDestory(Queue* pq);
void QueueDestory(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head, *next;
while (cur) {
next = cur->next;
free(cur);
cur = NULL;
cur = next;
}
pq->head = pq->tail = NULL;
}
入队列
void QueuePush(Queue* pq, QDataType x);
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
if (newNode == NULL) {
printf("malloc fail\n");
exit(-1);
}
newNode->data = x;
newNode->next = NULL;
if (pq->head == NULL) {
pq->head = pq->tail = newNode;
} else {
pq->tail->next = newNode;
pq->tail = newNode;
}
}
出队列
void QueuePop(Queue* pq);
void QueuePop(Queue* pq)
{
assert(pq && pq->head && pq->tail);
if (pq->head->next == NULL) {
free(pq->head);
pq->head = pq->tail = NULL;
} else {
QueueNode* next = pq->head->next;
free(pq->head);
pq->head = NULL;
pq->head = next;
}
}
是否为空
bool QueueEmpty(Queue* pq);
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
大小
size_t QueueSize(Queue* pq);
size_t QueueSize(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
size_t size = 0;
while (cur) {
cur = cur->next;
size++;
}
return size;
}
返回队首的元素
QDataType QueueFront(Queue* pq);
QDataType QueueFront(Queue* pq)
{
assert(pq && pq->head);
return pq->head->data;
}
返回队尾的元素
QDataType QueueTail(Queue* pq);
QDataType QueueTail(Queue* pq)
{
assert(pq && pq->tail);
return pq->tail->data;
}
习题
1.移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
【思路】使用双指针,一个指针指向最终的结果,一个指针进行遍历数组
两个指针初始时都指向最初位置
遍历数组的那个指针,如果等于那个值,则下标直接加一。
如果不相等,将遍历的下标值赋给指向最终结果的值 ,并且两个下标同时加1
int removeElement(int* nums, int numsSize, int val){
int src = 0, dst = 0;
while(src < numsSize) {
if(nums[src] == val) {
src++;
} else {
nums[dst++] = nums[src++];
}
}
return dst;
}
2.删除有序数组中的重复项
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。将最终结果插入 nums 的前 k 个位置后返回 k 。不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
【思路】双指针
一个指针src,和一个指针dst,src进行遍历,dst进行存储最终结果
如果src 和前一个相等,则src自增
如果src和前一个不相等,将src - 1那个位置的值赋给dst位置
最后将最后一个元素赋给dst,dst并自增,因为最后一个无论是否和前一个相等或不相等都不能将那个值存进去,所以需要手动存进去
int removeDuplicates(int* nums, int numsSize){
int dst = 0, src = 1;
while(src < numsSize) {
if(nums[src] == nums[src - 1]){
src++;
} else if (nums[src] != nums[src - 1]) {
nums[dst] = nums[src - 1];
dst++;
src++;
}
}
nums[dst] = nums[src - 1];
return dst + 1;
}
3.合并两个数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
【思路】双指针
使用两个指针一个p1,p1指向第一个数组的最后一个元素;一个p2,p2指针指向第二个数组的最后一个元素。
如果p1对应的元素比p2对应的元素大,将p1放进去,同时p1自减
如果p2对应的元素比p1对应的元素大,将p2放进去,同时p2自减
最后将还有元素的全部按照顺序放在剩下的位置种
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
int p1 = m -1, p2 = n -1, dst = n + m - 1;
while (p1 >= 0 && p2 >= 0) {
if (nums1[p1] > nums2[p2]) {
nums1[dst--] = nums1[p1--];
} else {
nums1[dst--] = nums2[p2--];
}
}
while (p1 >= 0) {
nums1[dst--] = nums1[p1--];
}
while (p2 >= 0) {
nums1[dst--] = nums2[p2--];
}
}
4.移除链表元素
给你一个链表的头节点head和一个整数val,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点
【思路】
设置三个指针,一个pre,一个cur,一个next。
pre初始时指向NULL,cur初始时指向头节点,next为cur的下一个节点。
如果cur的值不等于val,cur和pre同时向后移动。
如果cur的值等于val,cur向后移动,pre的下一个指向cur的后一个。
如果开头的值就是val的话,头节点向后移动,---- 判断 ,pre等于NULL,且cur -> val == val
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode *pre = NULL, *cur = head, *next;
while(cur) {
if (cur->val != val) {
pre = cur;
cur = cur->next;
} else {
next = cur->next;
if (pre == NULL) {
free(cur);
head = cur = next;
} else {
free(cur);
pre->next = next;
cur = next;
}
}
}
return head;
}
5.反转链表
给你单链表的头节点 head,请你反转链表,并返回反转后的链表。
【思路】
若要反转链表,可以按照链表的顺序进行头插,然后将最后的头节点返回
比如 :
1 -> 2 -> 3 -> 4 -> NULL
定义一个新的头节点,作位反转后的头节点,在定义连个指针,分别保存当前值(用于头插)和下一个值(防止找不到)。
newHead = NULL, cur = 1的节点, next = 1的下一个节点
第一次头插 1 -> NULL , newHead = 1, cur = 2, next = 3
第二次头插 2 -> 1-> NULL, newHead = 2, cur = 3, next = 4
第三次头插 3 -> 2 -> 1 -> NULL, newHead = 3, cur = 4, next = NULL
第四次头插 4 -> 3 -> 2 -> 1 -> NULL, newHead = 4, cur = NULL 结束
6.链表的中间节点
给定一个头节点为 head的非空单链表,返回链表的中间节点。如果有两个中间节点,则返回第二个中间节点。
【思路】
7.链表中倒数第K个节点
输入一个链表输出链表中倒数第K个节点
【思路】
8.合并两个有序链表
将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的
【思路】
9.链表分隔
现有一链表的头指针 ListNode * pHead,给一定值x,编写一段代码将所有小于x的节点排在其余节点之前,且不改变原来的数据顺序,返回重新排列后链表的头指针。
【思路】
10.链表的回文结构
对于一个链表,请设定一个时间复杂度为O(N),额外空间复杂度为O(!)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900.
【思路】
11.相交链表
给你两个链表的头节点headA 和headB,请你找出并返回两个单链表相交的起始节点。如果两个不存在相交节点,返回null
【思路】
12.环形链表
给你链表的头节点head,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪next指针再次到达,则链表中存在环。为了表示给定链表中的环,测评系统内部使用整数pos来表示链表尾链接道链表中的位置(索引从0开始)。注意:pos不作为参数进行传递。仅仅是为了标识链表的实际情况
如果链表存在环,则返回true;否则false
【思路】
13.环形链表II
给定一个链表的头节点head ,返回链表开始入环的第一个节点。如果链表无环,则返回null。
如果链表中有某个节点,可以通过连续跟踪next 指针再次到达,则链表中存在环。为了表示给定链表中的环,评测系统内部使用整数 pos来表示链表尾链接到链表中的位置(索引从0开始)。如果 pos 是-1,则该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了识别链表的实际情况。
不允许修改链表
【思路】
14.复制带随机指针的链表
给你一个长度为n的链表,每个节点包含一个额外增加的随机指针 random , 则该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由n个全新节点组成,其中每个节点的值都设为其对应原节点的值。新节点的 next指针和random指针也应该指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应该指向原链表中的节点。
【思路】
15.有效的括号
给定一个只包括'(', ')', '[', ']', '{', '}'的字符串s,判断字符串是否有效。
有效字符串需要满足:
1.左括号必须相同类型的右括号
2.左括号必须以正确的顺序闭合
【思路】
16.用栈实现队列
请你仅用两个栈实现先进先出的队列。队列应当支持一般队列支持的所有操作。(push、pop、peek、empty)
请实现MyQueue类:
- void push(int x) 将元素x推到队列的末尾
- int pop()从队列的开头移除并返回元素
- int peek() 返回队列开头的元素
- boolean empty()如果队列为空,返回True;否则返回false
【思路】
17.用队列实现栈
请你仅仅使用两个队列实现一个先进后出的栈,并支持普通栈的全部四种操作(push、top、pop、empty)
实现Mystac类:
- void push(int x) 将元素x压入栈顶
- int pop() 移除并返回栈顶元素
- int top() 返回栈顶元素
- boolean empty() 如果栈为空,返回true;否则返回false
【思路】
18.设计循环队列
设计你的循环队列实现。循环队列是一种线性数据结构,其操作表现基于(FIFO)(先进先出)原则并且队尾被接连在队首之后形成一个循环。它也被称为"环形缓冲器"
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值
你的实现应该支持如下操作:
MyCircularQueue(k):
构造器,设置队列长度为 k 。Front
: 从队首获取元素。如果队列为空,返回 -1 。Rear
: 获取队尾元素。如果队列为空,返回 -1 。enQueue(value)
: 向循环队列插入一个元素。如果成功插入则返回真。deQueue()
: 从循环队列中删除一个元素。如果成功删除则返回真。isEmpty()
: 检查循环队列是否为空。isEmpty()
: 检查循环队列是否为空。
【题解】