我们首先得创建一个单链表
class ListNode {
public int val;
public ListNode next;
public ListNode(){
}
public ListNode(int val){
this.val=val;
}
}
我们直接写的是方法,就不具体实现链表了
题目链接
思路
①:从后到前,指针指向前一个节点
②:头插法
class Solution {
public ListNode reverseList(ListNode head) {
ListNode newHead=null;
ListNode cur=head;
ListNode prev=null;
while(cur!=null){
//保存cur.next
ListNode curNext=cur.next;
if(curNext==null){
newHead=cur;
}
cur.next=prev;
prev=cur;
cur=curNext;
}
return newHead;
}
}
头插法
class Solution {
public ListNode reverseList(ListNode head) {
ListNode prev=null;
while (head!=null) {
ListNode cur=head;
head=head.next;
cur.next=prev;
prev=cur;
}
return prev;
}
}
题目链接
思路
快慢指针,快的一次走两步,慢的一次走一步,循环结束,慢指针就是中间节点
class Solution {
public ListNode middleNode(ListNode head) {
if(head==null)return null;
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
slow=slow.next;
}
return slow;
}
}
题目链接
思路
①:求链表长度len,走len-k步
②:快慢指针:先让fast走k步,循环结束,slow就是倒数第k个节点
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode cur=head;
int len=getLength(head);
//走(len-k)步
for(int i=0;i<(len-k);i++){
cur=cur.next;
}
return cur;
}
//求链表长度
public int getLength(ListNode head){
int count=0;
ListNode cur=head;
while(cur!=null){
cur=cur.next;
count++;
}
return count;
}
}
快慢指针法
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode fast=head;
ListNode slow=head;
//先让fast走k步
for(int i=0;i<k;i++){
fast=fast.next;
}
while(fast!=null){
fast=fast.next;
slow=slow.next;
}
return slow;
}
}
题目链接
思路
因为传入函数的唯一参数为 要被删除的节点,所以我们直接和下一个节点交换
class Solution {
public void deleteNode(ListNode node) {
node.val=node.next.val;//换值
node.next=node.next.next;//换址
}
}
题目链接
思路
讲解全在代码中了
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head==null)return null;
//傀儡节点,新链表
ListNode newHead=new ListNode(-1);
ListNode tmp=newHead;
ListNode cur=head;
while(cur!=null){
//判断条件:值相等,还不能越界
if(cur.next!=null && cur.val==cur.next.val){
//可能有多个重复的节点
//链表结尾有连续相同的,可能会空指针
while(cur.next!=null&& cur.val==cur.next.val){
cur=cur.next;
}
//多走一步,走出重复数字的区域
cur=cur.next;
}else{
//把不重复的节点接到傀儡节点后面
tmp.next=cur;
cur=cur.next;
tmp=tmp.next;
}
}
//新链表结尾置空
tmp.next=null;
return newHead.next;
}
}
题目链接
讲解全在代码中了
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode cur=head;
while(cur!=null&&cur.next!=null){
//遇见相邻两个值相等的,就跳过这个节点,直接接上下一个节点
if(cur.val==cur.next.val){
cur.next=cur.next.next;
}else{
//不相等就继续遍历
cur=cur.next;
}
}
return head;
}
}
思路
找到要删除节点的前驱
public ListNode searchPrev(int key){
ListNode cur=this.head;
while(cur.next!=null){
if(cur.next.val==key){//cur是前驱,cur.next是要删除节点(下一个节点),cur.next.val是要删除节点的值
return cur;
}
cur=cur.next;//如果不等于,还要继续往后走
}
return null;//没找到返回null
}
//删除第一次出现关键字为key的节点
public void remove(int key){
//如果没节点
if(this.head==null){
return;
}
//如果是头结点
if(this.head.val==key){
this.head=this.head.next;
}
//正常删
ListNode prev=searchPrev(key);
if(searchPrev(key)==null){
System.out.println("没有key的前驱!");
}else{
ListNode del=prev.next;
prev.next=del.next;
}
题目链接
思路
利用前驱跳过要删除的节点
class Solution {
public ListNode removeElements(ListNode head, int val) {
if(head==null)return null;
ListNode prev=head;
ListNode cur=prev.next;
//从第二个点开始判断,有前驱好判断
while(cur!=null){
if(cur.val==val){
prev.next=cur.next;
cur=cur.next;
}else{
prev=cur;
cur=cur.next;
}
}
//再判断头结点
if(head.val==val){
head=head.next;
}
return head;
}
}
题目链接
思路
定义一个傀儡节点,相当于一个新链表
按照升序,把节点接到新链表中
注意:如果链表一长一短,在最后判断一下,把长链表的剩下部分直接接到新链表后面就好了
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null)return l2;
if(l2==null)return l1;
//傀儡节点
ListNode newHead=new ListNode(-1);
ListNode cur=newHead;
while(l1!=null && l2!=null){
if(l1.val>l2.val){
cur.next=l2;
cur=cur.next;
l2=l2.next;
}else{
cur.next=l1;
cur=cur.next;
l1=l1.next;
}
}
if(l1==null){
cur.next=l2;
}else{
cur.next=l1;
}
return newHead.next;
}
}
题目链接
思路
理解了上面的合并两个有序链表题的话,本题迎刃而解
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
ListNode res=null;
for(int i=0;i<lists.length;i++){
res=merge(res,lists[i]);
}
return res;
}
public ListNode merge(ListNode a,ListNode b){
if(a==null)return b;
if(b==null)return a;
ListNode newHead=new ListNode(-1);
ListNode cur=newHead;
ListNode list1=a;
ListNode list2=b;
while(list1!=null && list2!=null){
if(list1.val<list2.val){
cur.next=list1;
list1=list1.next;
cur=cur.next;
}else{
cur.next=list2;
list2=list2.next;
cur=cur.next;
}
}
if(list1!=null){
cur.next=list1;
}else{
cur.next=list2;
}
return newHead.next;
}
}
思路很简单,注释全在代码中了
class Solution {
public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) {
ListNode node=list1;
//让node跑到删除部分的头的左边一个
for(int i=0;i<a-1;i++){
node=node.next;
}
//nodeNext是删除部分的头
ListNode nodeNext=node.next;
//循环跑完.nodeNext在删除部分的结尾
for(int i=0;i<b-a+1;i++){
nodeNext=nodeNext.next;
}
//node的下一个接上list2
node.next=list2;
while(node.next!=null){
node=node.next;
}
//list2结尾接上list1剩下的
node.next=nodeNext;
return list1;
}
}
题目链接
思路
创建新链表,让符合条件的节点接在后面,代码中注释很详细
class Solution {
public ListNode partition(ListNode head, int x) {
ListNode bs=null;//小数据部分始端(beforestart)
ListNode be=null;//小数据部分末端(beforeend)
ListNode as=null;//大数据部分始端(afterstart)
ListNode ae=null;//大数据部分末端(afterend)
ListNode cur=head;
while(cur!=null){
if(cur.val<x){
if(bs==null){
//两个点都指向放进来的节点
bs=cur;
be=cur;
}else{
//有数据了进行尾插
be.next=cur;
be=be.next;
}
}else{
if(as==null){
as=cur;
ae=cur;
}else{
ae.next=cur;
ae=ae.next;
}
}
//让cur也移动着
cur=cur.next;
}
/**
* 有两个小情况
* ①:最后要返回小数据部分的头,但是如果小数据部分没有数据,就会出错
* ②:找不到尾结点
*/
if(bs==null){
return as;
}
be.next=as;
if(as!=null){
ae.next=null;
}
return bs;
}
}
题目链接
思路
我们先把链表存在数组里
排序后再转成链表就可以了
class Solution {
public ListNode sortList(ListNode head) {
if(head==null||head.next==null){
return head;
}
int len=getLength(head);
int[] ans=toArray(head,len);
Arrays.sort(ans);
//创建个傀儡节点当头
ListNode newHead=new ListNode(-1);
ListNode cur=newHead;
//再把数组转成链表
for(int i=0;i<=len-1;i++){
newHead.val=ans[i];
if(i<len-1){
//因为我们只创建了一个傀儡节点,所以接着创建
newHead.next=new ListNode(-1);
newHead=newHead.next;
}
}
return cur;
}
//求链表长度
public int getLength(ListNode head){
int len=0;
while(head!=null){
len++;
head=head.next;
}
return len;
}
//把链表存在数组里
public int[] toArray(ListNode head,int len){
int[] nums=new int[len];
int i=0;
while(head!=null){
nums[i]=head.val;
head=head.next;
i++;
}
return nums;
}
}
题目链接
思路
代码中注释超详细
class Solution {
public ListNode insertionSortList(ListNode head){
//判空
if(head==null){
return null;
}
ListNode node=new ListNode(-1);//创建傀儡节点,目的:方便插入
node.next=head;//与头结点连接起来
ListNode lastSorted=head;//
ListNode cur=head.next;
while(cur!=null){
if(lastSorted.val<=cur.val){//排好序
lastSorted=lastSorted.next;
}else{
ListNode prev=node;//要插入节点的前驱
while(prev.next.val<=cur.val){
/**
* prev是要插入节点的前一个
* 如果prev下一个节点的val值大于cur的val值
* 就要把cur插入prev后面
* 这也是循环的退出条件
*/
prev=prev.next;
}
//完成对cur的插入
lastSorted.next=cur.next;//相当于是把要插入的节点在原位置删除,前后再连接起来
cur.next=prev.next;
prev.next=cur;
}
cur=lastSorted.next;//新的cur一直在lastSorted的后面
}
return node.next;
}
}
题目链接
思路
快慢指针
1.找中间结点
2.中点到结尾进行反转
3.head从前往后走,slow从后往前走
class Solution {
public boolean isPalindrome(ListNode head) {
if(head==null)return false;
//定义快慢指针
ListNode fast=head;
ListNode slow=head;
//奇偶数节点,两种情况
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
slow=slow.next;
}
//中点到结尾进行反转
ListNode cur=slow.next;
while(cur!=null){
ListNode curNext=cur.next;
cur.next=slow;
slow=cur;
cur=curNext;
}
//head从前往后
//slow从后往前
while(head!=slow){
if(head.val!=slow.val){
return false;
}
//偶数节点情况
if(head.next==slow){
return true;
}
head=head.next;
slow=slow.next;
}
return true;
}
}
题目链接
思路
代码中注释很详细
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA==null || headB==null) return null;
int lenA=0;
int lenB=0;
ListNode l1=headA;
ListNode l2=headB;
//求长度
while(l1!=null){
l1=l1.next;
lenA++;
}
while(l2!=null){
l2=l2.next;
lenB++;
}
//一定要指回来,不然下面会出现空指针异常
l1=headA;
l2=headB;
int len=lenA-lenB;
if(len<0){
//l1永远放最长的链表
len=lenB-lenA;
l1=headB;
l2=headA;
}
//让长链表先走len步
while(len!=0){
l1=l1.next;
len--;
}
//往后遍历(相同了就停止)
while(l1!=l2){
l1=l1.next;
l2=l2.next;
}
return l1;
}
}
题目链接
思路
快慢指针
public class Solution {
public boolean hasCycle(ListNode head) {
if(head==null)return false;
ListNode fast=head;
ListNode slow=head;
while(fast!=null && fast.next!=null){
fast=fast.next.next;
slow=slow.next;
//如果两指针相遇,就说明一定有环
if(fast==slow){
return true;
}
}
return false;
}
}
题目链接
思路
遍历链表每一个节点,并记录下来,遇到之前遍历过的,就可以得到入环节点
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode cur=head;
Set<ListNode> set=new HashSet<>();
while(cur!=null){
if(set.contains(cur)){
return cur;
}else{
set.add(cur);
}
//别忘了让cur动着
cur=cur.next;
}
return null;
}
}
题目链接
思路
定义一个“前驱”,反转它后面的两个节点
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode newHead=new ListNode(-1);
newHead.next=head;
ListNode tmp=newHead;
while(tmp.next!=null && tmp.next.next!=null){
ListNode node1=tmp.next;
ListNode node2=tmp.next.next;
//反转
tmp.next=node2;//tmp接上了第二个节点
node1.next=node2.next;//第一个节点指向了第二个节点的后面(此时node1已经在后面了)
node2.next=node1;//把node2接在新的node1上
tmp=node1;//tmp再指向新的node1,进行下一个循环,交换下两个节点
}
return newHead.next;
}
}
题目链接
思路
首尾相连闭合成环解决问题
class Solution {
public ListNode rotateRight(ListNode head, int k) {
//判空
if(k==0 || head==null || head.next==null){
return head;
}
int len=1;
ListNode cur=head;
//这里求链表长度
//判断cur.next不为空,也可以保证下面※行不为空
while(cur.next!=null){
cur=cur.next;
len++;
}
//pos是新链表
int pos=len-k%len;
※cur.next=head;//首尾相连成环
//这个循环结束,cur的位置在翻转后的链表的尾结点
while(pos>0){
cur=cur.next;
pos--;
}
//把环断开,尾结点置为空
ListNode newHead=cur.next;
cur.next=null;
return newHead;
}
}
题目链接
思路
1.先将l1和l2头节点的值加起来赋值给新链表的头节点
2.遍历两个链表,只要有一个链表还没有遍历到末尾,就继续遍历
3.每次遍历生成一个当前节点cur的下一个节点,其值为两链表对应节点的和再加上当前节点cur产生的进位
4.更新进位后的当前节点cur的值
5.循环结束后,因为首位可能产生进位,因此如果cur.val是两位数的话,新增一个节点
6.返回头节点
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head=new ListNode(l1.val+l2.val);
ListNode cur=head;
while(l1.next!=null || l2.next!=null){
l1=l1.next!=null ? l1.next : new ListNode();
l2=l2.next!=null ? l2.next : new ListNode();
cur.next=new ListNode(l1.val+l2.val+cur.val/10);
cur.val%=10;
cur=cur.next;
}
if(cur.val>=10){
cur.next=new ListNode(cur.val/10);
cur.val%=10;
}
return head;
}
}
能看到这儿的人,都是月薪20k+的大佬
开怀大笑了,家人们