每日十道算法

最近发现了一个挺厉害的人工智能学习网站,内容通俗易懂,风趣幽默,感兴趣的可以点击此链接进行查看:床长人工智能教程

 废话不多说,请看正文!

1、链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点

 

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        if(head == null){
            return null;
        }
        ListNode node = new ListNode();
        node.next = head;
        ListNode slow = node;
        ListNode fast = node;

        for(int i = 0; i <= k; i++){
            fast = fast.next;
        }
        while(fast != null){
            slow = slow.next;
            fast = fast.next;
        }
        return slow.next;
    }
}

 2、返回倒数第 k 个节点

实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int kthToLast(ListNode head, int k) {
        if(head == null){
            return -1;
        }
        ListNode node = new ListNode();
        node.next = head;
        ListNode slow = node;
        ListNode fast = node;
        for(int i = 0; i <= k; i++){
            fast = fast.next;
        }
        while(fast != null){
            slow = slow.next;
            fast = fast.next;
        }
        return slow.next.val;
    }
}

3、交换链表中的节点

给你链表的头节点 head 和一个整数 k 。

交换 链表正数第 k 个节点和倒数第 k 个节点的值后,返回链表的头节
点(链表 从 1 开始索引)。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode swapNodes(ListNode head, int k) {
        if(head == null){
            return null;
        }
        ListNode p1 = head;
        int m = k;
		for (int i = 0; i < m - 1; i++) {
			p1 = p1.next;
		}        
        int n = k;
        ListNode node = new ListNode();
        node.next = head;
        ListNode slow = node;
        ListNode fast = node;
        for(int i = 0; i <= n; i++){
            fast = fast.next;
        }
        while(fast != null){
            slow = slow.next;
            fast = fast.next;
        }
        int temp = slow.next.val;
        slow.next.val = p1.val;
        p1.val = temp;
        return head;
    }
}

 4、链表相交

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的
起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:

 

时间复杂度:$O(n)
空间复杂度:$O(1) 

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */


public class Main {
    public ListNode main(ListNode headA, ListNode headB) {
        if(headA == null || headB == null){
            return null;
        }
        ListNode p1 = headA;
        ListNode p2 = headB;
        while(p1 != p2){
            if(p1 != null){
                p1 = p1.next;
            }else{
                p1 = headB;
            }
            if(p2 != null){
                p2 = p2.next;
            }else{
                p2 = headA;
            }
        }
        return p1;        
    }
}

 5、环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表
中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表
示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则
在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链
表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false 。

时间复杂度:$O(n)
空间复杂度:$O(1) 

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */


/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){
                return true;
            }
        }
        return false;      
    }
}

6、环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在
环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到
链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:
pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

时间复杂度:$O(n)
空间复杂度:$O(1) 

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */


public class Main {
    public ListNode main(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){
                ListNode p1 = head;
                ListNode p2 = fast;
                while(p1 != p2){
                    p1 = p1.next;
                    p2 = p2.next;
                }
                return p1;
            }
        }
        return null;
    }
}

 7、反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

时间复杂度:$O(n)
空间复杂度:$O(1) 

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */


class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode p1 = null;
        ListNode p2 = head;
        ListNode p3 = null;
        while(p2 != null){
            p3 = p2.next;
            p2.next = p1;
            p1 = p2;
            p2 = p3;
        }
        return p1;
    }
}

8、反转链表 II

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left
 <= right 。请你反转从位置 left 到位置 right 的链表节点,返回
反转后的链表 。

 时间复杂度:$O(n)
空间复杂度:$O(1)

class Solution {
	public ListNode reverseBetween(ListNode head, int left, int right) {
		// 因为头节点有可能发生变化,使用虚拟头节点可以避免复杂的分类讨论
		ListNode node = new ListNode(-1);
		node.next = head;

		ListNode p1 = node;
		// 第 1 步:从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点
		// 建议写在 for 循环里,语义清晰
		for (int i = 0; i < left - 1; i++) {
			p1 = p1.next;
		}

		// 第 2 步:从 p1 再走 right - left + 1 步,来到 right 节点
		ListNode rightNode = p1;
		for (int i = 0; i < right - left + 1; i++) {
			rightNode = rightNode.next;
		}

		// 第 3 步:切断出一个子链表(截取链表)
		ListNode leftNode = p1.next;
		ListNode p2 = rightNode.next;

		// 注意:切断链接
		p1.next = null;
		rightNode.next = null;

		// 第 4 步:同第 206 题,反转链表的子区间
		reverseList(leftNode);

		// 第 5 步:接回到原来的链表中
		p1.next = rightNode;
		leftNode.next = p2;
		return node.next;
	}
    public ListNode reverseList(ListNode head) {
        ListNode p1 = null;
        ListNode p2 = head;
        ListNode p3 = null;
        while(p2 != null){
            p3 = p2.next;
            p2.next = p1;
            p1 = p2;
            p2 = p3;
        }
        return p1;
    }
}

9、从尾到头打印链表

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。 

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        ListNode p1 = null;
        ListNode p2 = head;
        ListNode p3 = null;
        int len = 0;
        while(p2 != null){
            p3 = p2.next;
            p2.next = p1;
            p1 = p2;
            p2 = p3;
            len++;
        }
        int[] res = new int[len];
        int count = 0;
        while(p1 != null){
            res[count++] = p1.val;
            p1 = p1.next;
        }
        return res;
    }
}

10、K个一组翻转链表

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        if (head == null || head.next == null){
            return head;
        }
        //定义一个假的节点。
        ListNode node=new ListNode(0);
        //假节点的next指向head。
        // node->1->2->3->4->5
        node.next=head;
        //初始化p1和rightNode都指向node。p1指每次要翻转的链表的头结点的上一个节点。rightNode指每次要翻转的链表的尾节点
        ListNode p1=node;
        ListNode rightNode=node;

        while(rightNode.next != null){
            //循环k次,找到需要翻转的链表的结尾,这里每次循环要判断rightNode是否等于空,因为如果为空,rightNode.next会报空指针异常。
            //node->1->2->3->4->5 若k为2,循环2次,rightNode指向2
            for(int i = 0; i < k && rightNode != null; i++){
                rightNode=rightNode.next;
            }
            //如果rightNode==null,即需要翻转的链表的节点数小于k,不执行翻转。
            if(rightNode == null){
                break;
            }
            //先记录下rightNode.next,方便后面链接链表
            ListNode p2=rightNode.next;
            //然后断开链表
            rightNode.next=null;
            //记录下要翻转链表的头节点
            ListNode leftNode=p1.next;
            //翻转链表,p1.next指向翻转后的链表。1->2 变成2->1。 node->2->1
            p1.next=reverse(leftNode);
            //翻转后头节点变到最后。通过.next把断开的链表重新链接。
            leftNode.next=p2;
            //将p1换成下次要翻转的链表的头结点的上一个节点。即leftNode
            p1=leftNode;
            //翻转结束,将rightNode置为下次要翻转的链表的头结点的上一个节点。即leftNode
            rightNode=leftNode;
        }
        return node.next;
    }
    //链表翻转
    // 例子:   head: 1->2->3->4
    public ListNode reverse(ListNode head) {
        //单链表为空或只有一个节点,直接返回原单链表
        if (head == null || head.next == null){
            return head;
        }
        ListNode p1 = null;
        ListNode p2 = head;
        ListNode p3 = null;
        while(p2 != null){
            p3 = p2.next;
            p2.next = p1;
            p1 = p2;
            p2 = p3;
        }
        return p1;
    }


}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zpeien

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值