1. 反转链表
1.1 题目描述
输入一个链表,反转链表后,输出新链表的表头。
1.2 示例1
输入
{1,2,3}
返回值
{3,2,1}
1.3 核心代码实现
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head == null || head.next == null)
return head;
ListNode pre = null; //pre记录当前节点的前一个节点
ListNode next = null; //next记录当前节点的下一个节点
while(head != null){
next = head.next; //先用next指针记录当前节点的下一个节点地址
head.next = pre; //让当前节点与链表断开并指向前一个节点pre,反转
pre = head; //继续下一个节点
head = next;
}
return pre;
}
}
2. 合并两个排序的链表
2.1 题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
2.2 示例1
输入
{1,3,5},{2,4,6}
返回值
{1,2,3,4,5,6}
2.3 核心代码实现
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
/*
//递归,若list1<list2,则list1为新序列的头节点,继续比较list1.next与list2
if(list1 == null) return list2;
if(list2 == null) return list1;
if(list1.val < list2.val){
list1.next = Merge(list1.next, list2);
return list1;
}else{
list2.next = Merge(list2.next, list1);
return list2;
}
*/
ListNode head = new ListNode(0); //初始化一个节点值为0的空节点
ListNode result = head;
while(list1 != null && list2 != null){
if(list1.val < list2.val){
result.next = list1;
list1 = list1.next;
}else{
result.next = list2;
list2 = list2.next;
}
result = result.next;
}
if(list1 != null) //list1的节点多于list2的
result.next = list1;
if(list2 != null) //list2的节点多于list1的
result.next = list2;
return head.next;
}
}
3. 复杂链表的复制
3.1 题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
3.2 核心代码实现
import java.util.HashMap;
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
public class Solution {
public RandomListNode Clone(RandomListNode pHead){
if(pHead == null) return pHead;
RandomListNode result = new RandomListNode(pHead.label); //result作为新链表的头结点
RandomListNode p1 = pHead; //p1获取链表的头结点
RandomListNode p2 = result; //p2获取新链表的头结点
HashMap<RandomListNode, RandomListNode> map = new HashMap<>();
//第一次遍历,利用pHead将所有值存入map,避免被替换,需要new
while(pHead != null){
map.put(pHead, new RandomListNode(pHead.label));
pHead = pHead.next;
}
//第二次遍历
while(p1 != null){
p2.next = map.get(p1.next); //拷贝映射关系
p2.random = map.get(p1.random);
p1 = p1.next; //同时移动p1和p2
p2 = p2.next;
}
return result; //返回拷贝后的头结点
}
}
4. 孩子们的游戏(圆圈中最后剩下的数)
4.1 题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
如果没有小朋友,请返回-1
4.2 示例1
输入
5,3
返回值
3
4.3 核心代码实现
import java.util.ArrayList;
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if(n == 0) return -1; //没有小盆友时
/*
//数学归纳法:设f[n]表示有n个小盆友时最后获奖小盆友的编号,则f[1]=0;f[n]=(f[n-1]+m)%n;
int index = 0; //只有一个小盆友时,编号为0的小盆友获奖
//有两个或两个以上小盆友时
for(int i = 2; i <= n; i++){
index = (index + m) % i;
}
return index;
*/
//方法二:借助list的add和remove方法
ArrayList<Integer> list = new ArrayList<>();
for(int i = 0; i < n; i++){
list.add(i); //模拟初始小盆友序列
}
int index = 0; //index记录要删除的元素位置,初始化为0
while(list.size() > 1){
//当不止一个小盆友时
index = (index + m - 1) % list.size(); //开始新一轮报数
list.remove(index); //将唱歌小盆友对应的编号从list中移除
}
return list.get(0); //返回list中仅剩的一个元素
}
}
5. 删除链表中重复的结点
5.1 题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
5.2 示例1
输入
{1,2,3,3,4,4,5}
返回值
{1,2,5}
5.3 核心代码实现
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead){
if(pHead == null || pHead.next == null) return pHead;
ListNode head = new ListNode(0); //定义一个哨兵结点head
head.next = pHead;
ListNode pre = head;
ListNode cur = head.next;
while(cur != null){
if(cur.next != null && cur.val == cur.next.val){
//遇到重复结点时,cur继续往前走,直到不同后跳出循环
while(cur.next != null && cur.val == cur.next.val){
cur = cur.next;
}
//退出循环后,当前的cur仍指向重复结点
cur = cur.next; //让cur指向下一个不重复的结点
pre.next = cur; //让pre连接不重复的结点
}else{
pre = cur;
cur = cur.next;
}
}
return head.next;
}
}
6. 链表中环的入口结点
6.1 题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
6.2 核心代码实现
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
//提示:画图理解
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead){
if(pHead == null || pHead.next == null) return null;
ListNode fastP = pHead; //定义一个快指针并初始化
ListNode slowP = pHead; //定义一个慢指针并初始化
while(fastP != null && fastP.next != null){
fastP = fastP.next.next; //快指针每次跑两个element
slowP = slowP.next; //慢指针每次跑一个element
if(fastP == slowP){
//快指针与慢指针相遇
ListNode slowP2 = pHead; //重新定义一个慢指针从头开始跑
while(slowP2 != slowP){
//两个慢指针同步跑,直到两者相遇
slowP2 = slowP2.next;
slowP = slowP.next;
}
return slowP; //返回任意一个慢指针,此时两者指向同一结点
}
}
return null;
}
}