LeetCode刷题知识点笔记---数组、链表篇(Java语言)

在这里插入图片描述
一、数组篇
在这里插入图片描述

1、基本用法

//定义一个一维数组,nums.length为数组长度
int[] ans = new int[nums.length];
//定义一个大小为m*n的二维数组
int[][] matrix = new int[m][n];
//获取行数
int lenY = matrix.length;
//获取列数---4int lenX = matrix[0].length;
//用sort函数对数组ans进行排序
Arrays.sort(ans);
//创建一个整数List集合
List<Integer> vals = new ArrayList<Integer>();
//把链表值复制到集合中
vals.add(currentNode.val);
//取数组最后一个元素的索引,size()方法为求集合长度
int back = vals.size() - 1;
//取数组某个索引的值
vals.get(front)//vals为定义的数组,front为索引下标

2、二分查找(折半查找)
为避免溢出,可以将middle = (left + right) / 2 改为 middle = (right - left) / 2 + left;
(1)区间左闭右闭
right = array.size() - 1;
两个临界条件:
left <= right
right = middle - 1 或 left = middle + 1
(2)区间左闭右开
right = array.size();
两个临界条件:
left < right
right = middle 或 left = middle + 1
3、移除元素
注意:由于数组在内存中存储的方式,所以数组中的元素不能删除,只能覆盖。
(1)库函数
erase()函数
时间复杂度:O(n)
(2)暴力法
两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组
(3)双指针(快慢指针)思路
时间复杂度:O(n)
定义两个指针:
快指针:指向新数组所需要的元素
慢指针:指向新数组的下标值
然后将快指针获取到的元素值赋给慢指针
4、长度最小的子数组
注:子数组默认是数组中连续的非空元素序列,但二维数组不是。
在这里插入图片描述

(1)暴力法
两层for循环
(2)双指针(滑动窗口)
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
for循环里的变量表示的是滑动窗口的终止位置
滑动窗口的关键是如何移动起始位置
具体思路看https://programmercarl.com/0209.%E9%95%BF%E5%BA%A6%E6%9C%80%E5%B0%8F%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84.html#%E6%80%9D%E8%B7%AF

二、链表篇
在这里插入图片描述

哨兵结点在链表中用处很大,一定要记得考虑。
1、基础用法
初始化一个空节点,初始赋值为0,指针指向为list

ListNode list = new ListNode(0);

初始化一个空节点,初始赋值为0,并且list的下一个next指针指向head,指针指向为list

ListNode list = new ListNode(0,head);

定义一个空链表

ListNode list=null;

通常定义一个空节点还需要有节点的next指针指向,否则只是定义一个空节点

ListNode list = new ListNode(0,head);
//或者
ListNode list = new ListNode(0);
list.next=head;

虚拟头节点

//为链表head创建一个虚拟头结点
ListNode dummy=new ListNode(-1);
dummy.next=head;
ListNode p=dummy;

2、反转链表(P206)
双指针法

// 双指针
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode cur = head;
        ListNode temp = null;
        while (cur != null) {
            temp = cur.next;// 保存下一个节点
            cur.next = prev;
            prev = cur;
            cur = temp;
        }
        return prev;
    }
}

递归方式
一般来说,当子问题和原问题具有相同的结构时,考虑自上而下的递归。如合并两个有序链表P21

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        // 链表为空时直接返回,链表不为空则到返回最后一个节点
        if(!head || !head->next) {
            return head;
        }
        // newHead先指向最后一个节点,注意此时参数是倒数第二个节点
        // 这一步很精妙,每一次newHead都是指向空指针(链表为空)或保留在原链表中的最后一个节点(链表不空),作用就是返回新的头结点
        ListNode* newHead = reverseList(head->next);
        // 最后一个节点指向倒数第二个节点
        head->next->next = head;
        // 倒数第二个节点的下一节点置空。此时倒数第三个节点仍指向倒数第二个节点,下一次递归中将倒数第二个节点下一节点指向倒数第三个节点,不断重复这一过程
        head->next = nullptr;
        return newHead;
    }
};

头插法

        //使用头插法
        ListNode dummy = new ListNode(0);//创建一个新结点,参数值为0代表链表为空
        ListNode p = dummy, cur = head;
        while(cur != null){
            //从head摘下一个头
            ListNode t = cur;
            cur = cur.next;     //cur移到下一个
            t.next = p.next;    //头插法插入
            p.next = t;
        }
        return dummy.next;

迭代方式

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode curr = head;
        while (curr != null) {
            ListNode next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
}

3、常见双指针用法
(1)倒数第k个元素
设有两个指针 p 和 q,初始时均指向头结点。首先,先让 p 沿着 next 移动 k 次。此时,p 指向第 k+1个结点,q 指向头节点,两个指针的距离为 k 。然后,同时移动 p 和 q,直到 p 指向空,此时 q 即指向倒数第 k 个结点。
在这里插入图片描述
(2)获取中间元素
设有两个指针 fast 和 slow,初始时指向头节点。每次移动时,fast向后走两次,slow向后走一次,直到 fast 无法向后走两次。这使得在每轮移动之后。fast 和 slow 的距离就会增加一。设链表有 n 个元素,那么最多移动 n/2 轮。当 n 为奇数时,slow 恰好指向中间结点,当 n 为 偶数时,slow 恰好指向中间两个结点的靠前一个(可以考虑下如何使其指向后一个结点呢?)。
在这里插入图片描述
(3)是否存在环
总结快慢指针的特性 —— 每轮移动之后两者的距离会加一。
当一个链表有环时,快慢指针都会陷入环中进行无限次移动,然后变成了追及问题。想象一下在操场跑步的场景,只要一直跑下去,快的总会追上慢的。当两个指针都进入环后,每轮移动使得慢指针到快指针的距离增加一,同时快指针到慢指针的距离也减少一,只要一直移动下去,快指针总会追上慢指针。
如果存在环,如何判断环的长度呢?方法是,快慢指针相遇后继续移动,直到第二次相遇。两次相遇间的移动次数即为环的长度。
4、链表相加
简单来说,就是求两个链表交点节点的指针。
注意:交点不是数值相等,而是指针相等。
5、环形链表Ⅱ(P142)
出处:代码随想录
(1)判断链表是否有环
可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。

为什么fast 走两个节点,slow走一个节点,有环的话,一定会在环内相遇呢,而不是永远的错开呢

首先第一点:fast指针一定先进入环中,如果fast指针和slow指针相遇的话,一定是在环中相遇,这是毋庸置疑的。

那么来看一下,为什么fast指针和slow指针一定会相遇呢?

可以画一个环,然后让 fast指针在任意一个节点开始追赶slow指针。

会发现最终都是这种情况, 如下图:
在这里插入图片描述
fast和slow各自再走一步, fast和slow就相遇了

这是因为fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。
请添加图片描述

(2)如果有环,怎么找到这个环的入口
在这里插入图片描述
在这里插入图片描述
请添加图片描述
那么 n如果大于1是什么情况呢,就是fast指针在环形转n圈之后才遇到 slow指针。

其实这种情况和n为1的时候 效果是一样的,一样可以通过这个方法找到 环形的入口节点,只不过,index1 指针在环里 多转了(n-1)圈,然后再遇到index2,相遇点依然是环形的入口节点。

  • 13
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值