跟着carl哥的第三天
大三就是好,学过数据结构重新写关于链表的理解起来就是不难,只是有点老了。虽然起步比别人有点慢,但是坚持就是胜利!!
链表基础
先上单向链表(节点)模板
//单向链表节点
public class ListNode {
//单向链表的节点由两方面组成
//值+下一个链表节点的地址
int val; //链表节点的值
ListNode next; //当前链表节点的下一链表节点地址
ListNode() {}
//构造方法,方便传参用
public ListNode(int val) {
this.val = val;
}
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
再来一个双向链表(节点)模板
//双向链表节点
public class ListNodePlus {
int val;
ListNodePlus prev; //当前节点的上一节点
ListNodePlus next; //当前节点的下一节点
public ListNodePlus() {
}
public ListNodePlus(int val) {
this.val = val;
this.prev = this.next = null;
}
public ListNodePlus(int val, ListNodePlus prev, ListNodePlus next) {
this.val = val;
this.prev = prev;
this.next = next;
}
//加右节点
void addRight(ListNodePlus node) {
//将原本节点的下一节点地址给现在要插入的节点
node.next = this.next;
//如果下一节点的地址不为空,则说明还有下一节点
if (node.next != null) {
//则将下一节点的上一节点指针指向node(相互绑定)
node.next.prev = node;
}
//将原本节点的下一节点指针指向node
this.next = node;
//将node的上一节点指针指向原本节点
node.prev = this;
}
//删除右节点
void delRight() {
//直接将本节点的下一节点指针指向下一节点的下一节点
this.next = this.next.next;
//如果现在的下一节点不是空的话,就将下一节点的上一届点指针指向现在节点
if (this.next != null) {
this.next.prev = this;
}
}
}
大家要先学会怎么增加右节点跟删除右节点,这样子到时候做题遇到类似的题都用这个规则去做,这样子就不会被绕乱了(实不相瞒当时被绕了2小时) 然后学会了双向链表的增删可以去写一下单向链表的,应该差不多一样。
先看第一道题
leetcode203.移除链表元素
删除链表中等于给定值 val 的所有节点。
示例1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例2:
输入:head = [], val = 1
输出:[]
示例3:
输入:head = [7,7,7,7], val = 7
输出:[]
提示:
(记得你可以在链表的表头添加一个哑节点(没用的节点),这样子就使得头结点的删除跟其他节点一样)
大家把上面双向链表中的删除元素理解了就能轻轻松松把这道题干掉了
答案:::
public class test01 {
//定义输入输出
public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
public static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
//二分查找
private static int search(int[] nums, Integer target) {
//设置两个变量 分别表示左右两个边界
int left = 0;
int right = nums.length-1;
//循环
while(left <= right) {
//设置中间值,用来定位target属于左边的一半还是右边的一半
int mid = (left + right) / 2;
//如果属于左边的一半 则右边界right移动到刚刚的中间值mid
if (target <= nums[mid]) {
right = mid;
//如果属于右边的一半,则把左边界前移到中间值+1(因为上边是<=,所以nums[mid]属于左边界)
//则左边界变成mid+1
} else {
left = mid + 1;
}
}
//如果到最后没有找到target,则返回-1
if (nums[left] != target)
return -1;
return left;
}
public static void main(String[] args) throws IOException {
//读入一行,并按照空格拆分
String s = in.readLine();
String[] str = s.split(" ");
//获取数组的长度
int n = str.length;
//读入一个数字,默认读入String 需要转为int
Integer target = Integer.parseInt(in.readLine());
//转为int数组
int[] nums = new int[n];
for (int i = 0; i < n; i ++) {
nums[i] = Integer.parseInt(str[i]);
}
//输出答案并冲刷管道,关闭管道
int count = search(nums, target);
out.write(count);
out.flush();
out.close();
}
}
再看第二道题
leetcode707.设计链表
在链表类中实现这些功能:
- get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
- addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
- addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
- addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
- deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
示例 :
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3
linkedList.get(1); //返回2
linkedList.deleteAtIndex(1); //现在链表是1-> 3
linkedList.get(1); //返回3
思路:其实这道题只需要你引用上面的刚刚双向链表中的增删右节点的方法,一定一定要没开始前就定好规则,例如删右节点就删,不要自己再写一个在下一节点删除左节点) 然后就是index是从0开始的
先给大家定义好链表
public class MyLinkedList {
private ListNodePlus head, tail;
private int size;
public MyLinkedList() {
//链表中的所有节点都是 0-index 的
//初始化
size = 0;
head = new ListNodePlus(-1); //虚拟头节点
tail = new ListNodePlus(-1, head, null);
head.next = tail;
}
}
答案:
public class MyLinkedList {
private ListNodePlus head, tail;
private int size;
public MyLinkedList() {
//链表中的所有节点都是 0-index 的
//初始化
size = 0;
head = new ListNodePlus(-1); //虚拟头节点
tail = new ListNodePlus(-1, head, null); //虚拟尾节点
head.next = tail;
}
//获取链表中第 index 个节点。如果索引无效,则返回null
public ListNodePlus getNode(int index) {
//如果index < 0 或者 index >= size,则不符合条件,返回null
if (index < 0 || index >= size)
return null;
//从头节点开始遍历
ListNodePlus currentNode = head;
//因为i = 0是虚拟头节点,所以链表第index位是index+1开始
for (int i = 0; i <= index; i ++) {
currentNode = currentNode.next;
}
return currentNode;
}
//获取链表中第 index 个节点的值。如果索引无效,则返回-1
public int get(int index) {
ListNodePlus node = getNode(index);
return node == null ? -1 : node.val;
}
//在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
public void addAtHead(int val) {
head.addRight(new ListNodePlus(val));
size ++; //链表大小加1
}
//将值为 val 的节点追加到链表的最后一个元素。
public void addAtTail(int val) {
tail.prev.addRight(new ListNodePlus(val));
size ++;
}
//在链表中的 第index个节点之前 添加值为val的节点。如果index等于链表的长度,则该节点将附加到链表的末尾。
// 如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
public void addAtIndex(int index, int val) {
//先考虑特殊条件,最后再写普通插入
//如果 index 大于链表长度,则不会插入节点。
if (index <= size) {
//如果index小于0,则在头部插入节点。
if (index <= 0) {
addAtHead(val);
//如果index等于链表的长度,则该节点将附加到链表的末尾。
} else if (index == size) {
addAtTail(val);
} else {
getNode(index).prev.addRight(new ListNodePlus(val));
size ++;
}
}
}
//如果索引 index 有效,则删除链表中的第 index 个节点(第index+1位)。
public void deleteAtIndex(int index) {
ListNodePlus node = getNode(index);
if (node != null) {
node.prev.delRight();
size --;
}
}
}
最后一道链表
leetcode206.反转链表
反转一个单链表。
示例:
- 输入:1->2->3->4->5->NULL
- 输出: 5->4->3->2->1->NULL
思路:
链表的反转:可以根据我前天学得双指针算法的思想,用一个current指针指向head节点,然后pre指针指向null,然后两个指针同时往后遍历,然后修改方向就可以了
遍历的条件是什么呢? 然后两个指针就能直接遍历吗? 两个指针该以什么样的顺序才能找到下一节点呢? 都要考虑哦!!!
答案:
public class test03 {
public static void main(String[] args) {
System.out.println("hello world");
}
public ListNode reverseList(ListNode head) {
ListNode current = head;
ListNode pre = null;
ListNode temp = null; //作为中间值,用来防止current找不到下一节点
//遍历结束条件
while(current != null) {
//先将current的下一节点地址赋值给temp保存
temp = current.next;
//将current的下一节点指针指向前一节点(翻转)
current.next = pre;
//pre指针往后移一位
pre = current;
//将temp指针往后移一位
current = temp;
}
//因为temp已经null了,返回pre
return pre;
}
}
自我总结
重新复习了一下链表,有种熟悉又陌生的感觉
朋友仍需努力 一起加油
兄弟萌冲啊!
大概就是这样,大家懂了吗 有什么不懂的评论区评论或者私信我吧!!