输入一个链表,从尾到头打印链表每个节点的值
/**
* public class ListNode {
* int val;
* ListNode next = null;
* ListNode(int val) {
* this.val = val;
* }
* }
*/
//方法1:栈思想:先进后出
public class Solution {
public ArrayList printListFromTailToHead(ListNode listNode)
{
Stackstack=new Stack();
ListNode pNode=listNode;
//入栈
while(pNode!=null){
stack.push(pNode.val);
pNode=pNode.next;
}
//出栈
ArrayList al=new ArrayList<>();
while(!stack.isEmpty()){
al.add(stack.pop());
}
return al;
}
}
//方法2:递归思想
public class Solution {
ArrayListal=new ArrayList<>();
public ArrayList printListFromTailToHead(ListNode listNode)
{
if(listNode!=null){
this.printListFromTailToHead(listNode.next);
al.add(listNode.val);
}
return al;
}
}
输入一个链表,输出该链表中倒数第k个结点。
public class FindKthToTail {
public ListNode FindKthToTail(ListNode head,int k){
//定义两个指针,p1,p2,
//倒数第k个,即正数走n-k,总共n-1步,则p1先走k-1步,
// 然后p1,p2一起走,p1到结尾,此时p2刚好走n-k,到达倒数第k个节点
if(head==null||k<=0)
return null;
ListNode p1=head;
ListNode p2=head;
for (int i = 0; i
输入一个链表,反转链表**后,输出新链表的表头
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head==null)
return null;
//当前节点是head,pre为当前节点的前一节点,next为当前节点的下一节点
//需要pre和next的目的是让当前节点从pre->head->next1->next2变成pre<-head next1->next2
//即pre让节点可以反转所指方向,但反转之后如果不用next节点保存next1节点的话,此单链表就此断开了
//所以需要用到pre和next两个节点
//1->2->3->4->5
//1<-2<-3 4->5
ListNode pre=null;
ListNode next=null;
ListNode cur=head;
//pre, head, next
while(cur!=null)//不是while(cur.next!=null)
{
//先用next保存head的下一个节点的信息,保证单链表不会因为失去head节点的原next节点而就此断裂
// cur=head;
next=cur.next;
//保存完next,就可以让head从指向next变成指向pre了,
cur.next=pre;
//head指向pre后,就继续依次反转下一个节点
//让pre,head,next依次向后移动一个节点,继续下一次的指针反转
pre=cur;
cur=next;////不是while(cur.next!=null)
}
//如果cur为null的时候,pre就为最后一个节点了,但是链表已经反转完毕,pre就是反转后链表的第一个节点
//直接输出pre就是我们想要得到的反转后的链表
return pre;
}
}
输入两个单调递增的链表,输出两个链表合成后的链表**,当然我们需要合成后的链表满足单调不减规则。
public class Solution {
//方法1:递归思想:先看头结点,谁的头节点小,谁就作为输出链表
public ListNode Merge(ListNode list1,ListNode list2)
{
if(list1==null)
return list2;
if(list2==null)
return list1;
//ListNode pMergedHead=null;
if(list1.val<=list2.val){
// pMergedHead=list1;
// pMergedHead.next=Merge(list1.next,list2);
list1.next=Merge(list1.next,list2);
return list1;
}else if(list2.val
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
/*
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 null;
//1. 复制单链表
RandomListNode pCur=pHead;//pCur指向头结点
while(pCur!=null)//p指向头结点,只要p不为空,就一直循环
{
RandomListNode node=new RandomListNode(pCur.label);//创建新节点,值和pCur相同
node.next=pCur.next;//插入新节点
pCur.next=node;
pCur=node.next;//pCur指向下一个待复制节点
}
//2. 复制random域
pCur=pHead;//pCur指向头结点;
while(pCur!=null)
{
if(pCur.random!=null)//有些节点本身就没有random,再访问random的后继就会出现空指针!
{
pCur.next.random=pCur.random.next;//A A1 B B1 ,若A->B,则A1->B1 复制节点的random指向 当前节点的random的后一个节点
}
pCur=pCur.next.next;//继续遍历下一个
}
//3.拆分链表:有难度啊,这个拆分啊
RandomListNode pCloneHead=pHead.next;//复制链表
RandomListNode temp=pHead;//过渡链表对象,借助此指针,将原链表拆分出去
pCur=pHead;
//将原链表拆分出去
while(pCur.next!=null)
{
temp=pCur.next;//指向当前节点的下一个
pCur.next=temp.next;
pCur=temp;
}
return pCloneHead;
}
}
输入两个链表,找出它们的第一个公共结点。
//方法一:很巧秒的思路,不用遍历长度
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode cur1=pHead1;
ListNode cur2=pHead2;
while(cur1!=cur2){////如果没有相交,那么curA==null,curB==null时相等,返回null
cur1=(cur1==null)?pHead2:cur1 .next;
cur2=(cur2==null)?pHead1:cur2.next;
}
return cur1;
}
}
//方法2:先遍历两个链表得到长度,较长链表先走差值步,然后同时出发遍历链表,直到到达公共点,或者为Null,时间复杂度0(M+N)
一个链表中包含环,请找出该链表的环的入口结点
public class Solution {
//方法1:HashSet集合的无重复性,要寻找环的入口节点,遍历节点的时候,遇到的第一个重复节点肯定是入环口节点,
//所以定义一个Set(不能添加重复数字),添加失败时 即返回环入口节点
//ListNode 是个指针,比较的时候是比较是否指向同一个对象,也就是该指针的值(既对象的地址)是否相同,
//和所指对象无关,因此,即使1->1->2又重复数字的也可以,因为对象不同,环是又指向了同一个对象
public ListNode EntryNodeOfLoop(ListNode pHead){
if(pHead==null)
return null;
ListNode pNode=pHead;
HashSetpSet=new HashSet();
while (pNode!=null){
if(!pSet.add(pNode))
return pNode;
else
pNode=pNode.next;
}
return null;
}
/*
//方法2:快慢指针
public ListNode EntryNodeOfLoop(ListNode pHead){
if(pHead==null)
return null;
ListNode pFast=pHead;
ListNode pSlow=pHead;
//先判断是否有环,两个指针一个fast、一个slow同时从一个链表的头部出发
//fast一次走2步,slow一次走一步,如果该链表有环,两个指针必然在环内相遇
while(pFast!=null&&pFast.next!=null){
pFast=pFast.next.next;
pSlow=pSlow.next;
if(pFast==pSlow)
break;//退出整个while循环
}
if(pFast==null||pFast.next==null)
return null;
//此时只需要把其中的一个指针重新指向链表头部,另一个不变(还在环内),
//这次两个指针一次走一步,相遇的地方就是入口节点。
pFast=pHead;
while(pFast!=pSlow)
{
pFast=pFast.next;
pSlow=pSlow.next;
}
return pFast;//否则再次相遇的节点即环入口,自己画画
}
*/
}
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
public class Solution {
public ListNode deleteDuplication(ListNode pHead)
{
//链表为空,或者只有一个节点
if(pHead==null||pHead.next==null)
return pHead;
//1、如果当前头节点是重复节点
if(pHead.val==pHead.next.val)
{
ListNode pNode=pHead.next;
// 跳过值与当前结点相同的全部结点,找到第一个与当前结点不同的结点
while (pNode!=null&&pNode.val==pHead.val)
{
pNode=pNode.next;
}
return deleteDuplication(pNode);
}
//2、 当前头结点不是重复结点
else{
pHead.next=deleteDuplication(pHead.next);// 保留当前结点,从下一个结点开始递归
return pHead;
}
}
}
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
class Solution {
public void deleteNode(ListNode node) {//输入:4-5-1-9,删除5
node.val=node.next.val;//将带删除节点替换为节点的下一个节点,4-1-1-9
node.next=node.next.next;//单链表的删除操作,4-1-9
}
}
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
public class IsPalindrome {
//方法1:用快慢指针找到中间元素位置,同时将前半部分入栈,即前半段节点保存在栈里,利用栈后进先出的特点,即从中间向两侧比较,和链表后半段进行比较
public boolean isPalindrome(ListNode head) {
//快慢指针,找出链表中间元素,压入栈
ListNode slow=head;
ListNode fast=head;
Stack stack=new Stack<>();
while (fast!=null&&fast.next!=null){
//入栈
stack.push(slow.val);
//快慢指针移动,慢指针依次走一步,快指针一次走两步,当快指针到达尾部,慢指针到达链表中间位置
slow=slow.next;
fast=fast.next.next;
}
//当链表长度为奇数时,跳过中间元素;如果是偶数,则不用跳过,直接比较即可
if(fast!=null){//fast==null时,是偶数
slow=slow.next;
}
//将后面元素和栈顶元素比较
while(slow!=null){
if(slow.val!=stack.pop()){
return false;
}else{
slow=slow.next;
}
}
return true;
}
/********************************************************************/
//方法2:用快慢指针找到链表中间元素,将链表后半段反转,前后段链表比较
public boolean isPalindrome(ListNode head) {//1-2-2-1 1-2-1
ListNode fast = head;
ListNode slow = head;
//快慢指针,快指针到达链表尾部或者尾部下一个时,慢指针指向链表中间
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
//当链表为奇数时,fast指向链表尾节点(fast!=null),慢指针需要跳过中间元素在反转链表
//当链表为偶数时,fast指向链表尾结点的下一个(fast==null),慢指针所指之后的链表反转,而无需跳过中间元素
if (fast != null) { // odd nodes: let right half smaller
slow = slow.next;
}
//将链表分成两段,快指针指向第一段头结点,慢指针将链表反转之后,指向反转后的链表头结点
slow = reverse(slow);//反转慢指针之后的链表 (fast)1->2 null<-2<-1(slow)
fast = head;
//快慢指针同时走,比较值是否相同
while (slow != null) {
if (fast.val != slow.val) {
return false;
}
fast = fast.next;
slow = slow.next;
}
return true;
}
//反转链表 prev slow next
public ListNode reverse(ListNode slow) {
ListNode prev = null;
while (slow != null) {
ListNode next = slow.next;//先保存下一个
slow.next = prev; //指向前一个
prev = slow; //prev和slow向后移动
slow = next;
}
return prev;
}
}
给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
特例:
输入:(5) + (5 )
输出:0->1
class Solution {
/*方法1: 相当于边相加边将每一位上计算出来的数字添加到链表中,
前一位的进位加到下一位数字和上。比如(2 -> 4 -> 3) + (5 -> 6 -> 4)中第二个对应数字相加
4+6=10,进位1加到下一位3+4上,即3+4+1=8
*/
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
if(l1==null)
return l2;
if(l2==null)
return l1;
//新建链表,填充节点
ListNode resHead=new ListNode(-1);
ListNode cur=resHead;
int curNum=0;//当前位数字和
int flag=0;//进位
while(l1!=null||l2!=null||flag!=0){//flag!=0为了计算特例[5] [5],输出[0,1]而不是[0]
//判断两个链表长度不一样时候,就补0对齐
int val1=(l1!=null)?l1.val:0;
int val2=(l2!=null)?l2.val:0;
curNum=val1+val2+flag;//当前位数字和
flag=curNum/10; //进位
cur.next=new ListNode(curNum%10);//和为12,将1进位,2添加到链表
cur=cur.next;
//计算链表上下一位对应数字
l1=l1!=null?l1.next:null;
l2=l2!=null?l2.next:null;
}
//返回链表
return resHead.next;
}
}
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。
请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。
示例 1:
输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL
示例 2:
输入: 2->1->3->5->6->4->7->NULL
输出: 2->3->6->7->1->5->4->NULL
说明:
应当保持奇数节点和偶数节点的相对顺序。
链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。
public class OddEvenList {
public ListNode oddEvenList(ListNode head) {
if(head==null)
return null;
//定义两个指针分别指向奇偶序号头部,遍历,当遍历到链表末尾时,将奇链表直接指向偶链表头部即可
ListNode odd=head,
even=head.next,
evenHead=head.next;//定义这个不随遍历变动的变量是为了一直指向偶数序号头部,便于奇尾部直接连上偶数头部
//1->null even==null
// 1->2->null even.next==null
// 1->2->3->null 奇数个链表节点时,even指向null,even==null,就该退循环
// 1->2->3->4->null 偶数个链表节点时,even指向4,但是even.next==null,就该退循环
while(even!=null&&even.next!=null){
odd.next=odd.next.next;
even.next=even.next.next;
odd=odd.next;
even=even.next;
}
odd.next=evenHead;
return head;
}
}
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0)
return null;
// PriorityQueue 是堆,默认小顶堆
/* PriorityQueue queue = new PriorityQueue(11, new Comparator() {
@Override
public int compare(ListNode o1, ListNode o2) {
return o1.val - o2.val;
}
});*/
PriorityQueue queue = new PriorityQueue<>((a, b) -> (a.val - b.val));
// 加入所有链表的第一个结点,非空
for (ListNode firstNode : lists)
if (firstNode != null)
queue.offer(firstNode);
ListNode head = new ListNode(-1);
ListNode cur = head;
while (!queue.isEmpty()) {
ListNode temp = queue.poll();
cur.next = temp;
cur = cur.next;
// 边取边加入
if (temp.next != null)
queue.offer(temp.next);
}
// 注意断链
cur.next = null;
return head.next;
}
}
####3个有序链表合并
public class Main {
//3个链表,第1个和第2个合并之后的链表再和第三个链表合并,即两两合并
public static ListNode Merge(ListNode[] nodes) {
ListNode res = nodes[0];
for (int i = 1; i < nodes.length; i++) {
res = Merge(res, nodes[i]);
}
return res;
}
//两个有序链表的合并
public static ListNode Merge(ListNode list1,ListNode list2)
{
if(list1==null)
return list2;
if(list2==null)
return list1;
ListNode pMergedHead=null;
if(list1.val<=list2.val){
pMergedHead=list1;
pMergedHead.next=Merge(list1.next,list2);
}else if(list2.val
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
//思想:归并排序:将链表分成两部分,分别排序,在合并
public ListNode sortList(ListNode head) {
if (head == null || head.next == null)
return head;
// step 1. 快慢指针将链表分成两段
ListNode prevSecond=null,
fast=head,
slow=head;
while(fast!=null&&fast.next!=null){
prevSecond=slow;//prev是第一段末尾,是第二段头结点slow的前一个
fast=fast.next.next;
slow=slow.next;
}
prevSecond.next=null;
// step 2. 两段分别排序
ListNode l1=sortList(head);
ListNode l2=sortList(slow);
// step 3. merge l1 and l2
return merge(l1,l2);
}
//将两个有序链表合成一个有序链表
ListNode merge(ListNode l1,ListNode l2){
ListNode head=new ListNode(-1);
ListNode p=head;
while(l1!=null&&l2!=null){
if(l1.val