学习目标:
- 203.移除链表元素
- 707.设计链表
- 206.反转链表
- 分布式常见面试题(下)
学习内容:
203.移除链表元素
题目链接 &&文章讲解
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点
不添加虚拟头节点
class Solution {
public ListNode removeElements(ListNode head, int val) {
//头节点为空
if (head == null) {
return head;
}
//头节点的值为val
while (head != null && head.val == val) {
head = head.next;
}
ListNode cur=head;
while(cur.next!=null){
if(cur.next.val==val){
cur.next=cur.next.next;
}else{
cur=cur.next;
}
}
return head;
}
}
虚拟头节点
class Solution {
public ListNode removeElements(ListNode head, int val) {
//头节点为空
if (head == null) {
return head;
}
//定义虚拟头节点
ListNode dummyHead=new ListNode();
ListNode cur=dummyHead;
dummyHead.next=head;
//判断cur的下一个节点是否为val
while(cur.next!=null){
if(cur.next.val==val){
cur.next=cur.next.next;
}else{
cur=cur.next;
}
}
return dummyHead.next;
}
}
- 直接使用原来的链表来进行删除操作: 由于头节点无前一个节点,因此移除头结点和移除其他节点的操作不同,需要单独操作,将头结点向后移动一位就可以,这样就从链表中移除了一个头结点
- 设置一个虚拟头结点在进行删除操作:虚拟头节点的next为头节点,因此头节点可以使用和移除链表其他节点的方式
707.设计链表
题目链接&&文章讲解
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList 类:
- MyLinkedList() 初始化 MyLinkedList 对象。
- int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
- void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
- void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
- void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
- void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。
单链表
class MyLinkedList {
//链表size
int size;
//虚拟头节点
ListNode head;
class ListNode{
int val;
ListNode next;
ListNode(){}
ListNode(int val) {
this.val=val;
}
}
public MyLinkedList() {
size=0;
head=new ListNode(0);
}
public int get(int index) {
//如果index非法,返回-1
if (index < 0 || index >= size) {
return -1;
}
ListNode cur=head;
for(int i=-1;i<index;i++){
cur=cur.next;
}
return cur.val;
}
public void addAtHead(int val) {
addAtIndex(0, val);
}
public void addAtTail(int val) {
addAtIndex(size, val);
}
public void addAtIndex(int index, int val) {
if(index>size){
return;
}
index = Math.max(0, index);
ListNode pre=head;
for(int i=0;i<index;i++){
pre=pre.next;
}
size++;
ListNode add=new ListNode(val);
add.next=pre.next;
pre.next=add;
}
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
size--;
if (index == 0) {
head = head.next;
return;
}
ListNode pre = head;
for (int i = 0; i < index ; i++) {
pre = pre.next;
}
pre.next = pre.next.next;
}
}
双链表
class MyLinkedList {
int size;
ListNode head;
ListNode tail;
class ListNode{
int val;
ListNode prev;
ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public MyLinkedList() {
size = 0;
head = new ListNode(0);
tail = new ListNode(0);
head.next = tail;
tail.prev = head;
}
public int get(int index) {
if(index<0||index>=size){
return -1;
}
ListNode cur;
//比较从head还是tail哪个更快找到目标
if(index+1<size-index){//head更快
cur=head;
for(int i=-1;i<index;i++){
cur=cur.next;
}
}else{//tail更快
cur=tail;
for(int i=size;i>index;i--){
cur=cur.prev;
}
}
return cur.val;
}
public void addAtHead(int val) {
addAtIndex(0, val);
}
public void addAtTail(int val) {
addAtIndex(size, val);
}
public void addAtIndex(int index, int val) {
if (index > size) {
return;
}
index = Math.max(0, index);
ListNode pred, succ;
if(index<size-index){
pred=head;
for(int i=-1;i<index-1;i++){
pred=pred.next;
}
succ=pred.next;
}else{
succ=tail;
for(int i=size;i>index;i--){
succ=succ.prev;
}
pred=succ.prev;
}
size++;
ListNode add=new ListNode(val);
add.prev=pred;
add.next=succ;
pred.next=add;
succ.prev=add;
}
public void deleteAtIndex(int index) {
if (index< 0||index>=size) {
return;
}
ListNode pred, succ;
if (index<size-index) {
pred=head;
for(int i=-1; i<index-1; i++) {
pred=pred.next;
}
succ=pred.next.next;
} else {
succ=tail;
for (int i=size; i>index+1; i--) {
succ=succ.prev;
}
pred=succ.prev.prev;
}
size--;
pred.next=succ;
succ.prev=pred;
}
}
206.反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表
图片来源:力扣题解
从前往后:
从后往前
双指针法
class Solution {
public ListNode reverseList(ListNode head) {
if(head==null || head.next==null){
return head;
}
ListNode cur=head;
ListNode temp;
ListNode pre=null;
while(cur!=null){
temp=cur.next;
cur.next=pre;
pre=cur;
cur=temp;
}
return pre;
}
}
递归法
//递归法1:从前往后
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(null,head);
}
public ListNode reverse(ListNode pre,ListNode cur){
if(cur==null){
return pre;
}
ListNode temp=cur.next;
cur.next=pre;
return reverse(cur,temp);
}
}
//递归法2:从后向前递归
class Solution {
public ListNode reverseList(ListNode head) {
// 边缘条件判断
if(head == null) return null;
if (head.next == null) return head;
//使用递归函数,一直递归到链表的最后一个结点,该结点就是反转后的头结点,记作 retretret
ListNode ret =reverseList(head.next);
//每次函数在返回的过程中,让当前结点的下一个结点的next指针指向当前节点
head.next.next = head;
//同时让当前结点的next指针指向null,从而实现从链表尾部开始的局部反转
head.next = null;
return ret;
}
}
分布式服务治理
- 服务治理:为什么需要服务注册与发现?
- 服务治理:分布式下如何进行配置管理?
- 服务治理:分布式事务解决方案有哪些?
- 服务治理:监控系统如何做?
- 服务治理:分布式下如何进行日志管理?
- Spring Cloud Alibaba 组件