203.移除链表元素 leetcode地址
原链表操作与虚拟头节点操作
/**
* 方法1
* 时间复杂度 O(n)
* 空间复杂度 O(1)
* @param head
* @param val
* @return
*/
public ListNode removeElements(ListNode head, int val) {
while(head!=null && head.val==val) {
head = head.next;
}
ListNode curr = head;
while(curr!=null && curr.next !=null) {
if(curr.next.val == val){
curr.next = curr.next.next;
} else {
curr = curr.next;
}
}
return head;
}
/**
* 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 removeElements(ListNode head, int val) {
ListNode dummy = new ListNode();
ListNode acc = new ListNode();
dummy.next = head;
acc = dummy;
while(acc.next != null){
if(acc.next.val == val){
acc.next = acc.next.next;
}else{
acc = acc.next;
}
}
return dummy.next;
}
}
注意:不管是原链表还是虚拟头结点链表都有需要新建一个虚拟链表用来遍历承接,因为原链表因为节点的移动已经不能返回,如果返回 原链表,则得到的是最后一个链表,如果返回head,head有可能被删除,就返回的 None。
acc = acc.next;就代表着链表往下走一个。
707.设计链表 leetcode地址
单向链表
class ListNode {
int val;
ListNode next;
ListNode(){}
ListNode(int val) {
this.val=val;
}
}
class MyLinkedList {
int size = 0;
ListNode head;
public MyLinkedList(){
size = 0;
head = new ListNode(0);
}
public int get(int index) {
if(index < 0 || index >= size){
return -1;
}
ListNode cueewntNode = head;
for(int i = 0;i <= index;i++){
cueewntNode = cueewntNode.next;
}
return cueewntNode.val;
}
public void addAtHead(int val) {
ListNode newNode = new ListNode(val);
newNode.next = head.next;
head.next = newNode;
size++;
}
public void addAtTail(int val) {
ListNode newNode = new ListNode(val);
ListNode cur = head;
while(cur.next != null){
cur = cur.next;
}
cur.next = newNode;
size++;
}
public void addAtIndex(int index, int val) {
if(index > size){
return;
}
ListNode perd = head;
for(int i = 0;i < index; i++){
perd = perd.next;
}
ListNode toAdd = new ListNode(val);
toAdd.next = perd.next;
perd.next = toAdd;
size++;
}
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
size--;
//因为有虚拟头节点,所以不用对Index=0的情况进行特殊处理
ListNode pred = head;
for (int i = 0; i < index ; i++) {
pred = pred.next;
}
pred.next = pred.next.next;
}
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList obj = new MyLinkedList();
* int param_1 = obj.get(index);
* obj.addAtHead(val);
* obj.addAtTail(val);
* obj.addAtIndex(index,val);
* obj.deleteAtIndex(index);
*/
双向链表
class ListNode{
int val;
ListNode next,prev;
ListNode() {};
ListNode(int val){
this.val = val;
}
}
class MyLinkedList {
//记录链表中元素的数量
int size;
//记录链表的虚拟头结点和尾结点
ListNode head,tail;
public MyLinkedList() {
//初始化操作
this.size = 0;
this.head = new ListNode(0);
this.tail = new ListNode(0);
//这一步非常关键,否则在加入头结点的操作中会出现null.next的错误!!!
head.next=tail;
tail.prev=head;
}
public int get(int index) {
//判断index是否有效
if(index>=size){
return -1;
}
ListNode cur = this.head;
//判断是哪一边遍历时间更短
if(index >= size / 2){
//tail开始
cur = tail;
for(int i=0; i< size-index; i++){
cur = cur.prev;
}
}else{
for(int i=0; i<= index; i++){
cur = cur.next;
}
}
return cur.val;
}
public void addAtHead(int val) {
//等价于在第0个元素前添加
addAtIndex(0,val);
}
public void addAtTail(int val) {
//等价于在最后一个元素(null)前添加
addAtIndex(size,val);
}
public void addAtIndex(int index, int val) {
//index大于链表长度
if(index>size){
return;
}
size++;
//找到前驱
ListNode pre = this.head;
for(int i=0; i<index; i++){
pre = pre.next;
}
//新建结点
ListNode newNode = new ListNode(val);
newNode.next = pre.next;
pre.next.prev = newNode;
newNode.prev = pre;
pre.next = newNode;
}
public void deleteAtIndex(int index) {
//判断索引是否有效
if(index>=size){
return;
}
//删除操作
size--;
ListNode pre = this.head;
for(int i=0; i<index; i++){
pre = pre.next;
}
pre.next.next.prev = pre;
pre.next = pre.next.next;
}
}
添加链表节点,先找到前驱节点,先连接再赋值,连接新建节点F指向应该与前驱节点一样,(1)f,next = c.next; ,再将值带入 c,next = f;
双向的话
-
newNode.next = pre.next;
- 新节点的
next
指向pre
的下一个节点,以保持链表结构
- 新节点的
-
pre.next.prev = newNode;
- 这一步在新节点的
next
被设置后进行,以确保后续节点的prev
指向新节点
- 这一步在新节点的
-
newNode.prev = pre;
- 确保新节点正确链接到前一个节点
-
pre.next = newNode;
- 最后一步将
pre
的next
指向新节点,完成插入。
- 最后一步将
如果顺序改变的后果
- 如果先执行
pre.next = newNode
,则pre.next.prev
将会指向新节点,而不是原来的后续节点,导致链表结构错误。 - 如果不先设置新节点的
next
,将无法正确更新后续节点的prev
指针。
总结
插入新节点的每一步都依赖于前一步的结果,因此必须保持当前的顺序,确保链表的双向链接正确且完整。
206.反转链表leetcode地址
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null, cur = head;
while(cur != null){
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
双指针,先把cur.next用temp记录下来,否则翻转时消失,然后然后反转,反转之后都向后移动一位。