个人主页:************星纭-CSDN博客****************
系列文章专栏:************Python****************
踏上取经路,比抵达灵山更重要!一起努力一起进步!
目录
题目一:环形链表
题目出处:. - 力扣(LeetCode)/
题目描述:这道题和上一期的环形链表很像只不过,一个是判断是否存在环,一个是求入环节点。
题解思路:采用双指针。定义两个指针fast,slow,fast一次走两步,slow一次走一步。
然后我们来分析一下这道题的数学关系。
相遇点指的是fast指针和slow指针第一次相遇的地方。
当fast和slow相遇的时候,fast行走的距离是slow的两倍,
slow:L + X
fast:2(L + X) = L + X + a * N。a指的是fast进入环后行走的圈数
L = (a - 1) * N + N - X;
N-X就是相遇点到环的距离。也就是说,如果fast从相遇出发,slow从起点出发,均每次走一步。两者第一次相遇的地方就是进入环的节点。
cpp
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode*fast = head,*slow = head;
while(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
if(fast == slow){
fast = head;
while(fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return fast;
}
}
return NULL;
}
题目二:删除有序数组中的重复项
题目出处:. - 力扣(LeetCode)
题目描述:给你一个 非严格递增排列 的数组 nums
,请你原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
这道题的要求是,原地删除,所以空间复杂度是O(1),因为这个数组是非严格递增的,重复的元素是挨在一起的,即相等的元素在数组中一定是连续的。
解题思路:使用双指针,定义两个指针src,dst.src表示下一个不同元素的下标,dst表示要填入的位置的下标。
dst开始指向0,src开始指向1.如果src和dst相同,src就加1。不相同,dst加1,然后移动元素,然后src也加1.
cpp
int removeDuplicates(int* nums, int numsSize) {
int src,dst;
src = 1;
dst = 0;
while(src < numsSize){
if(nums[src] != nums[dst]){
++dst;
nums[dst] = nums[src];
++src;
}
else{
src++;
}
}
int k = dst + 1;
return k;
}
题目三:有效的括号
题目出处:. - 力扣(LeetCode)
题目描述 :
这个题目的示例不够全面,
对于字符串s = "{[]}",这样的字符串也成立,s = "()[{}]"也是成立的,题目所给的示例会让人误以为匹配的括号必须连续,其实不是这样的,这是题目的一个问题。
题目思路:首先需要用C语言手动实现一个栈。
然后我们开始遍历整个字符串:当遇到左括号的时候,就入栈,遇到右括号的时候,就取出栈顶元素,进行匹配,如果不匹配说明这个字符串不有效,反之继续匹配 ,直到结束,该字符串就匹配。关于栈的代码参考前面的文章。
cpp
bool isValid(char* s) {
Stack st;
STInit(&st);
while (*s) {
if (*s == '(' || *s == '[' || *s == '{') {
STPush(&st, *s);
} else {
if (IsEmpty(&st)) {
STDestory(&st);
return false;
} else {
char ch = STTop(&st);
STPop(&st);
if ((ch == '(' && *s != ')') || (ch == '[' && *s != ']') ||
(ch == '{' && *s != '}')) {
STDestory(&st);
return false;
}
}
}
++s;
}
bool ret = IsEmpty(&st);
STDestory(&st);
return ret;
}
在每次使用return语句之前都要销毁这个栈,避免内存泄漏。
最后为什么是返回ret?
如果遇到这样的字符,不难发现根本没有进入循环,直接返回了true,这明显是不正确的,所以需要进行判断,栈是否为空,如果为空,所以左括号并没匹配完成,这个字符串无效。
题目四:用队列实现栈
题目出处:. - 力扣(LeetCode)
题目描述:
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push
、top
、pop
和 empty
)。
实现 MyStack
类:
void push(int x)
将元素 x 压入栈顶。int pop()
移除并返回栈顶元素。int top()
返回栈顶元素。boolean empty()
如果栈是空的,返回true
;否则,返回false
。
解题思路:
如果接下来我们要进行出栈,根据先进后出的规则,那么得到的第一个数据应该是5,可是仅仅在队列一中进行出队列入队列是达不到这样的效果的。
那么如何进行操作呢?
首先我们可以将1-4入队列到队列二中,这样队列一中就只剩下一个数据5了,此时出队列就是5.起到了先进后出的效果。如果还需要放入数据,很明显是放在队列二中,因为此时的队列一已经没有了数据了。
首先需要使用C语言实现一个队列。代码参考前面的文章。
初始化:
cpp
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&(pst->q1));
QueueInit(&(pst->q2));
return pst;
}
push:
cpp
void myStackPush(MyStack* obj, int x) {
if (!QueueEmpty(&(obj->q1))) {
QueuePush(&(obj->q1), x);
} else {
QueuePush(&(obj->q2), x);
}
}
新放入的数据,需要放在已经存放有数据的队列。如果两个队列都为空,那么此时随便放在那个队列中都可以。
删除数据:
cpp
int myStackPop(MyStack* obj) {
Queue* noempty = &(obj->q1);
Queue* empty = &(obj->q2);
if (!QueueEmpty(&(obj->q2))) {
noempty = &(obj->q2);
empty = &(obj->q1);
}
while (QueueSize(noempty) > 1) {
QueuePush(empty, QueueFront(noempty));
QueuePop(noempty);
}
int top = QueueFront(noempty);
QueuePop(noempty);
return top;
}
由于不确定,哪一个队列是空的,所以采用假设法进行判断,出数据的时候,需要将非空队列的数据的前n-1个全部转移到空队列中,留剩下一个出栈即可。
查看栈顶元素:
cpp
int myStackTop(MyStack* obj) {
if (!QueueEmpty(&(obj->q2))) {
return QueueBack(&(obj->q2));
} else {
return QueueBack(&(obj->q1));
}
}
实现的队列中是,含有查看队尾元素的,非空的队列的队尾元素就是栈顶元素。
判空与销毁:
cpp
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&(obj->q1)) && QueueEmpty(&(obj->q2));
}
void myStackFree(MyStack* obj) {
QueueDestory(&(obj->q1));
QueueDestory(&(obj->q2));
free(obj);
}
这里的判空,是判断两个队列都是否为空。
题目五:用栈实现队列
题目出处;. - 力扣(LeetCode)
题目描述:
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push
、pop
、peek
、empty
):
实现 MyQueue
类:
void push(int x)
将元素 x 推到队列的末尾int pop()
从队列的开头移除并返回元素int peek()
返回队列开头的元素boolean empty()
如果队列为空,返回true
;否则,返回false
解题思路:
这道题与上一道题类似
只不过,需要判断空了,入栈的时候只需要把 数据固定存放在一个栈中,push栈,出栈的时候,先出pop栈中的数据,如果栈为空,把push栈的数据导过来。
初始化:
cpp
typedef struct {
ST pushst;
ST popst;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue*obj = (MyQueue*)malloc(sizeof(MyQueue));
STInit(&obj->pushst);
STInit(&obj->popst);
return obj;
}
入数据:
cpp
void myQueuePush(MyQueue* obj, int x) {
STPush(&obj->pushst,x);
}
查看数据:
cpp
int myQueuePeek(MyQueue* obj) {
if(STEmpty(&obj->popst)){
while(!STEmpty(&obj->pushst)){
STPush(&obj->popst,STTop(&obj->pushst));
STPop(&obj->pushst);
}
}
return STTop(&obj->popst);
}
首先需要判断popst中是否为空,如果不为空就直接返回即可,为空,就需要向pust中得到数据。
出数据:
cpp
int myQueuePop(MyQueue* obj) {
int front = myQueuePeek(obj);
STPop(&obj->popst);
return front;
}
出数据,直接使用peek函数先查看一下栈顶有没有元素,有才出,否则peek元素会先导数据。
判空与销毁:
cpp
bool myQueueEmpty(MyQueue* obj) {
return STEmpty(&obj->pushst) && STEmpty(&obj->popst);
}
void myQueueFree(MyQueue* obj) {
STDestory(&obj->popst);
STDestory(&obj->pushst);
free(obj);
}