数组
- 寻找数组中第二小的元素
这里有很多方法可以实现:
一个简单的解决方案是按递增顺序对数组进行排序,堆排、快排、归并排序等等都可以达到目的。排序数组中的前两个元素是两个最小的元素。这个解的时间复杂度是O(nlogn)。 关于排序算法后续会继续更新。
更好的解决方案是扫描数组两次。在第一次遍历中找到最小元素。让这个元素为x,在第二次遍历中,找到最小的元素大于x,这个解的时间复杂度是O(n)。
当然有更好的方法就是,在一次遍历中找到最小的两个数,时间复杂度为O(n)
初始化2个最小值,firstmin,secondmin
遍历所有元素,假如当前元素小于firstmin,那么将更新firstmin,secondmin.如果小于secondmin直接更新secondmin
- 找到数组中第一个不重复出现的整数
思路一:空间换时间,用另外一个数组存储每个数字出现的次数,但题目只需要找到第一个不重复出现的数,故浪费空间,时间复杂度O(N^2)
思路二:双循环,只要找到第一个不重复的数组,循环终止,内层循环只要发现有重复,即刻停止,时间复杂度是O(N^2)
- 合并两个有序数组
方法一: 将两个有序数组a和b容纳到另一个数组c中
算法思想:创建一个新的数组c,此数组的大小大于或等于已知两个数组之和。通过比较两个数组中的元素,谁小就把谁放到空数组中,知道其中一个数组为空,最后把剩下的数组全部放到新创建的始祖中。
方法二:有两个有序数组a和b,其中数组a的末尾有足够的空间容纳数组b,将数组b容纳到数组a中。
将两个数组从末尾开始往前比较,比完之后放在数组a的最后 - 重新排列数组中的正值和负值
可以将正数和负数分离,然后将正数和负数依次交换。
栈
-
使用栈计算后缀表达式
后缀表达式的求值规则为:从左到右扫描后缀表达式,如果遇到操作数,将其压入栈中,如果遇到操作符,则从栈中弹出两个操作数,计算结果,然后把结果入栈,直到遍历完后缀表达式,则计算完成,此时的栈顶元素即为计算结果。
如上的后缀表达式求值过程为:
(1) 初始化栈,栈顶指针为空;
(2) 遇到操作数a,入栈;
(3) 遇到操作数b,入栈;
(4) 遇到操作符*,弹出栈中两个元素,计算结果入栈;
(5) 遇到操作数c,入栈;
(6) 遇到操作符d,入栈;
(7) 遇到操作数e,入栈;
(8) 遇到运算符/,弹出栈中两个元素,计算结果入栈;
(9) 遇到操作符-,弹出栈中两个元素,计算结果入栈;
(10) 遇到操作数f,入栈;
(11) 遇到操作符*,弹出栈中两个元素,计算结果入栈;
(12) 遇到操作符+,弹出栈中两个元素,计算结果入栈; -
对栈的元素进行排序
从原始栈中以此弹出元素放入辅助栈中,每当将要压入的元素使得辅助栈不是升序排列时,就将辅助栈中的元素压入原始栈,直到辅助栈里的元素都小于当前要压入的元素,然后再压入当前元素。
因为只能使用一个辅助栈,我们每次取出栈内元素的时候,就要与辅助栈的元素比较,将所有大于取出元素的辅助站元素全部放回原栈之中,
一直重复这个操作,就能最后得到一个排好序的栈。 -
判断表达式是否括号平衡
利用栈的特点,遇到 左括号 ( ,则入栈;
遇到右括号 ) ,则出栈。注意出栈时检查栈是否为空。
队列
- 使用队列表示栈
栈实现队列:思路是有两个栈,一个用来放数据(数据栈),一个用来辅助(辅助栈)。数据添加时,会依次压人栈,取数据时肯定会取栈顶元素,但我们想模拟队列的先进先出,所以就得取栈底元素,那么辅助栈就派上用场了,把数据栈的元素依次弹出到辅助栈,但保留最后一个元素,最后数据栈就剩下了最后一个元素,直接把元素返回,这时数据栈已经没有了数据。最后呢,把辅助栈的元素依次压人数据栈,这样,我们成功取到了栈底元素。
队列实现栈
思路同上:有数据队列和辅助队列,模拟栈的先进后出,队列是队尾进队头出,也就是说每次取值要取队列的队尾元素,数据队列出队到辅助队列,留下最后一个元素返回,辅助队列再把元素出队到数据队列 - 对队列的前k个元素倒序
需要使用一个栈和一个新队列来实现反转队列前k个元素
1、从原始队列中取出k个元素,分别压入栈中
2、从栈中弹出所有元素逐个添加到一个新队列
3、把原始队列中剩下的元素也添加到新队列中
- 使用队列生成从1到n的二进制数
链表
-
反转链表
参考
反转单链表的思路:新建一个链表,然后原链表做头删操作,然后对新链表做头插操作。先定义一个新的head头指针标记为new head,初始化为空,然后定义一个node指针指向原链表头指针head。然后对原链表做头删操作,让head指向他的next的节点。然后这时候第一个节点已经被切割下来。然后再对新的链表做头插操作,让node指针的next指向新链表的头指针new head,然后完成头插操作。再更新new head作为新链表的头结点,再进行下一次循环。最终head指针指向原链表的结尾,退出循环,链表翻转完毕,最后返回新链表的newhead就可以。 -
检测链表中的循环
参考
两个方法:一个是哈希表法,还有一个是快慢指针,哈希表法就是把节点的内存地址作为哈希值来存储起来,然后每遍历一个节点,就在这个结构当中查找是否被遍历过,是否有重复,如果有重复,就说明链表当中有循环,如果直到遍历结束也没有重复,就代表没有循环。使用哈希表的话它的时间复杂度是O(n),因为遍历操作需要O(n),因为它需要额外的哈希表的存储空间,所以空间复杂度也是O(n)。第二个是快慢指针,就是先定一个快指针,每次移动两个节点,然后定一个慢指针,每次移动一个节点,如果快指针能够追上慢指针,就说明有循环,否则就没有。这个方法的时间复杂度是O(n),空间复杂度是O(1)。 -
返回链表倒数第N个节点
参考
两种方法,第一个方法就是计算链表的长度为L,然后打印链表从开头起的(L-N+1)个节点。第二个方法就是使用两个指针,分别是P1和P2,将他们先初始化指向head结点,然后P1从头开始移动N个节点,然后再将P1和P2一起往前移动,直到P1到达终点,就是指向空为止,这时候P2就是指向的是倒数第N个节点,返回P2即可. -
删除链表中的重复项
解题思路:
对链表进行循环遍历,以链表当前节点不为NULL作为循环的条件。遇到相同的值节点,将该值进行记录,并对链表中与记录值相同的节点进行删除操作。为了将剩余节点连接起来,需要记录删除节点的前一个节点,以此节点的next指向删除节点后的值不等节点。
解题步骤:
1.判断传入链表指针是否为空指针或空链表,如果为空直接返回;
2.定义两个节点,一个节点cur指针指向首节点,另一个节点prev指向空;
3.以cur!=NULL作为循环条件,进入while循环;
4.定义一个节点指针next指向cur->next,一个标志符needDelete初始化为false,表示节点是否需要删除
5.比较cur->data与next->data值的大小,如果相等,表示需要被删除,记录需要删除的值,进行删除操作,
如果不相等,改变prev与cur的指向
6.删除操作:定义Tobedel节点指针指向cur,如果Tobedel->value等于待删除的值,使next = Tobedel->next,删除节点 Tobedel,使Tobedel指向next ,进行下一波循环,直到Tobedel->value不等于待删除值结束 。结束 后,进行对prev的0 判空 ,为空指向*head = next,不为空执行prev->next = next,最后使cur指向next,进入下一波的循环。
图
实现广度和深度优先搜索
检查图是否为树
计算图的边数
找到两个顶点之间的最短路径
树
哈希表
在数组中查找对称键值对
追踪遍历的完整路径
查找数组是否是另一个数组的子集
检查给定的数组是否不相交