最近发现了一个挺厉害的人工智能学习网站,内容通俗易懂,风趣幽默,感兴趣的可以点击此链接进行查看:床长人工智能教程
废话不多说,请看正文!
1、两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。
你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交
换)。
时间复杂度:$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 swapPairs(ListNode head) {
ListNode node = new ListNode(0);
node.next = head;
ListNode p1 = node;
ListNode p2 = head;
ListNode p3;
while (p1.next != null && p1.next.next != null) {
p3 = p2.next.next; // 缓存 next
p1.next = p2.next; // 将 prev 的 next 改为 head 的 next
p2.next.next = p2; // 将 head.next(prev.next) 的next,指向 head
p2.next = p3; // 将head 的 next 接上缓存的temp
p1 = p2; // 步进1位
p2 = p2.next; // 步进1位
}
return node.next;
}
}
2、旋转链表
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k个位置。
时间复杂度:$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 rotateRight(ListNode head, int k) {
if(head == null){
return null;
}
ListNode p1 = head;
ListNode p2 = head;
int len = 1;
while(p1.next != null){
p1 = p1.next;
len++;
}
p1.next = head;
k = k % len;
while(++k < len){
p2 = p2.next;
}
ListNode p3 = p2;
p2 = p2.next;
p3.next = null;
return p2;
}
}
3、合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
时间复杂度:$O(n)
空间复杂度:$O(n)
/**
* 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 mergeTwoLists(ListNode l1, ListNode l2) {
ListNode node = new ListNode();
ListNode p1 = l1;
ListNode p2 = l2;
ListNode p3 = node;
while(p1 != null && p2 != null){
if(p1.val <= p2.val){
p3.next = new ListNode(p1.val);
p3 = p3.next;
p1 = p1.next;
}else{
p3.next = new ListNode(p2.val);
p3 = p3.next;
p2 = p2.next;
}
}
while(p1 != null){
p3.next = new ListNode(p1.val);
p3 = p3.next;
p1 = p1.next;
}
while(p2 != null){
p3.next = new ListNode(p2.val);
p3 = p3.next;
p2 = p2.next;
}
return node.next;
}
}
4、合并K个升序链表
合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
时间复杂度:$O(NlogK)
class Main {
public ListNode main(ListNode[] lists) {
if (lists == null || lists.length == 0) return null;
return merge(lists, 0, lists.length - 1);
}
private ListNode merge(ListNode[] lists, int left, int right) {
if (left == right) return lists[left];
int mid = left + (right - left) / 2;
ListNode l1 = merge(lists, left, mid);
ListNode l2 = merge(lists, mid + 1, right);
return mergeTwoLists(l1, l2);
}
private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;
if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1,l2.next);
return l2;
}
}
}
5、排序链表
给你链表的头结点 head ,请将其按升序排列并返回 排序后的链表 。
时间复杂度:$O(logn)
空间复杂度:$O(1)
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null){
return head;
}
// 快慢指针寻找中点
ListNode slow = head;
ListNode fast = head.next;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
ListNode temp = slow.next; // 中点的下一个
slow.next = null; // 打断链表
ListNode left = sortList(head);
ListNode right = sortList(temp);
ListNode node = new ListNode();
ListNode res = node;
while (left != null && right != null) { // 两个链表有序合并
if (left.val < right.val) {
node.next = left;
left = left.next;
} else {
node.next = right;
right = right.next;
}
node = node.next;
}
// 必有一个链表移动到了末尾
if(left == null){
node.next = right;
}else{
node.next = left;
}
return res.next;
}
}
6、回文链表
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
/**
* 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 boolean isPalindrome(ListNode head) {
if(head == null || head.next == null) return true;
// 找中点 1=>1 123=>2 1234=>2
ListNode p = mid(head);
ListNode q = p.next;
p.next = null;
// 翻转后半部分
q = reverse(q);
// 比对
boolean res = compare(head, q);
return res;
}
// 链表找中点,快慢指针法
ListNode mid(ListNode head) {
// 快慢指针寻找中点
ListNode slow = head;
ListNode fast = head.next;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
// 链表反转模板
ListNode reverse(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;
}
// 链表比对模板(len(l2) <= len(l1))
boolean compare(ListNode l1, ListNode l2) {
while(l2 != null) {
if(l1.val != l2.val) {
return false;
}
l1 = l1.next;
l2 = l2.next;
}
return true;
}
}
7、有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方组成的新数组,要求也按 非递减顺序 排序。
时间复杂度:$O(n)
空间复杂度:$O(n)
class Main {
public int[] main(int[] nums) {
if(nums.length == 0 || nums == null){
return null;
}
int[] res = new int[nums.length];
int left = 0;
int right = nums.length - 1;
int index = nums.length - 1;
while (left <= right) {
if (nums[left] * nums[left] < nums[right] * nums[right]) {
res[index] = nums[right] * nums[right];
index--;
right--;
} else {
res[index] = nums[left] * nums[left];
index--;
left++;
}
}
return res;
}
}
8、接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
时间复杂度:$O(n)
空间复杂度:$O(n)
class Main {
public int main(int[] height) {
if(height.length <= 2){
return 0;
}
int[] left = new int[height.length];
int[] right = new int[height.length];
int res = 0;
left[0] = height[0];
for(int i = 1; i < height.length; i++){
left[i] = Math.max(height[i],left[i - 1]);
}
right[height.length - 1] = height[height.length - 1];
for(int i = height.length - 2; i >= 0; i--){
right[i] = Math.max(height[i],right[i + 1]);
}
for(int i = 0; i < height.length; i++){
int count = Math.min(left[i],right[i]) - height[i];
if(count > 0){
res += count;
}
}
return res;
}
}
9、盛最多水的容器
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i,ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
时间复杂度:$O(n)
空间复杂度:$O(1)
class Solution {
public int maxArea(int[] height) {
int i = 0;
int j = height.length - 1;
int res = 0;
while(i < j){
if(height[i] < height[j]){
res = Math.max(res,(j - i) * height[i]);
i++;
}else{
res = Math.max(res,(j - i) * height[j]);
j--;
}
}
return res;
}
}
10、长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
时间复杂度:$O(n)
空间复杂度:$O(1)
class Solution {
// 滑动窗口
public int minSubArrayLen(int s, int[] nums) {
int left = 0;
int sum = 0;
int result = Integer.MAX_VALUE;
for(int right = 0;right < nums.length;right++){
sum += nums[right];
while (sum >= s) {
if ((right - left + 1) <= result) {
result = right - left + 1;
}
sum -= nums[left];
left++;
}
}
return result == Integer.MAX_VALUE?0:result;
}
}