最近发现了一个挺厉害的人工智能学习网站,内容通俗易懂,风趣幽默,感兴趣的可以点击此链接进行查看:床长人工智能教程
废话不多说,请看正文!
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;
}
}