203.移除链表元素
建议: 本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。
虚拟头节点解法:
public ListNode removeElements(ListNode head, int val) {
ListNode fake = new ListNode(-1,head);
ListNode cur = fake;
while(cur!=null){
while(cur.next!=null&&cur.next.val == val){
cur.next = cur.next.next;
}
cur = cur.next;
}
return fake.next;
}
这里遍历只使用了一个cur,时间复杂度为O(n)
- 假设链表中有 n 个节点,其中 m 个节点的值为
val
。 - 外部的 while 循环将从头节点开始遍历整个链表,因此它的时间复杂度为 O(n)。
- 内部的 while 循环用于找到值为
val
的节点,并将其删除。在最坏情况下,所有值为val
的节点都会被删除,而删除一个节点的操作只需要常数时间。 - 内部 while 循环的执行次数取决于链表中值为
val
的节点的数量 m,而不是整个链表的长度 n。 - 因此,内部 while 循环的时间复杂度为 O(m)。
综上所述,该段代码的时间复杂度为 O(n + m),其中 n 是链表的长度,m 是值为 val
的节点的数量。
不采用虚拟头节点:
public ListNode removeElements(ListNode head, int val) {
//不带虚拟头节点:
//处理头节点
while(head!=null && head.val==val){
head = head.next;
}
//此时头节点一定不为val
ListNode cur = head;
while(cur!=null){
while(cur.next!=null && cur.next.val==val){
cur.next = cur.next.next;
}
cur=cur.next;
}
return head;
}
非双while:
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode fake = new ListNode(-1,head);
ListNode cur = fake;
while(cur.next!=null){
if(cur.next.val == val){
cur.next = cur.next.next;
}else{
cur = cur.next;
}
}
return fake.next;
}
}
关键点在于if-else 当 当前节点不需要删除操作才继续遍历
707.设计链表
建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点
单链表:
class LinkList{
int val;
LinkList next;
public LinkList(){}
public LinkList(int val){
this.val = val;
}
}
class MyLinkedList {
int length;
LinkList head;
public MyLinkedList() {
length = 0;
//head为虚拟头节点
head = new LinkList(0);
}
public int get(int index) {
if (index > length-1 || index<0){
return -1;
}
LinkList cur = head;
for(int i=0;i<=index;i++){
cur = cur.next;
}
return cur.val;
}
public void addAtHead(int val) {
addAtIndex(0,val);
}
public void addAtTail(int val) {
addAtIndex(length,val);
}
public void addAtIndex(int index, int val) {
if (index > length || index < 0){
return ;
}
LinkList add = new LinkList(val);
LinkList cur = head;
for(int i=0;i<index;i++){
cur = cur.next;
}
add.next = cur.next;
cur.next = add;
length++;
}
public void deleteAtIndex(int index) {
if(index >= length || index < 0){
return ;
}
LinkList cur = head;
for(int i=0;i<index;i++){
cur = cur.next;
}
cur.next = cur.next.next;
length--;
}
}
双链表:
设计双链表需注意初始化,一点是初始化头和尾,一点是头尾之间需要"相连" 否则会出现 null.next
注意这两点后 即可顺利AC:
class ListNode {
int val;
ListNode pre;
ListNode next;
public ListNode(){}
public ListNode(int val){
this.val = val;
}
}
class MyLinkedList {
int size;
ListNode head,tail;
//注意双链表中 初始化需要两个节点,一头一尾,同时头尾之间的指针关系需要声明。
public MyLinkedList() {
size = 0;
head = new ListNode(-1);
tail = new ListNode(-1);
head.next = tail;
tail.pre = head;
}
public int get(int index) {
if(index<0 || index >= size){
return -1;
}
ListNode cur = head;
for(int i=0; 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<0 || index > size){
return ;
}
size++;
ListNode cur = head,pre = tail;
ListNode newNode = new ListNode(val);
for(int i=0;i<index;i++){
cur = cur.next;
}
//此处存在多种写法,注意要达到的目的(首尾相连) 与 对某节点的操作在对该节点赋值之前 即可
pre = cur.next;
cur.next = newNode;
newNode.pre = cur;
newNode.next = pre;
pre.pre = newNode;
}
public void deleteAtIndex(int index) {
if(index<0 || index >= size){
return ;
}
size--;
ListNode cur = head,pre = tail;
for(int i=0;i<index;i++){
cur = cur.next;
}
pre = cur.next.next;
cur.next = pre;
pre.pre = cur;
}
}
206.反转链表
只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表
双指针法:
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null, cur = head, temp;
while(cur != null){
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
递归:
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(null,head);
}
public ListNode reverse(ListNode pre,ListNode cur){
if(cur == null){
return pre;
}
else{
ListNode temp = cur.next; //保存下一节点
cur.next = pre; // 反转指针
return reverse(cur,temp);
}
}
}