文章链接:代码随想录
状态:逻辑不算难,基本都能自己写一次过
Leecode203:
关键是要添加一个虚拟头节点,操作比较方便。因为是单向列表,如果头节点刚好是删除的元素的话,就没办法操作了,所以要新建一个虚拟头节点来指向头节点。
代码:
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead=new ListNode(-1,head);
ListNode temp=dummyHead;
while (temp.next!=null){
if(temp.next.val==val){
temp.next=temp.next.next;
}else {
temp=temp.next;
}
}
return dummyHead.next;
}
Leecode707 设计链表
逻辑不难。但是一定要注意删除添加以及特殊条件判断的边界条件。不然可能导致一系列问题。相关注意事项在代码中标注。单链表和双链表的添加删除不同,一个遍历到目标地址的前一个,一个遍历到目标地址的当前。要注意单双链表虚拟头节点的设计,遍历的起始节点下标都是-1,get和增加删除方法都要注意。双链表初始化时虚拟头尾节点要相连,不然会出现空指针异常的情况。
代码:
//单链表
public class MyLinkedListSingle {
int size;
ListNode head;
public MyLinkedListSingle() {
this.size = 0;
this.head = new ListNode(0);
}
public int get(int index) {
//这里也要注意,下标从0开始,所以当index=size的时候实际上也是无效的下标。
if (index < 0 || index >= size) {
return -1;
}
ListNode temp = head;
for (int i = 0; i <=index; i++) {
temp = temp.next;
}
return temp.val;
}
public void addAtHead(int val) {
addAtIndex(0,val);
}
public void addAtTail(int val) {
addAtIndex(size,val);
}
public void addAtIndex(int index, int val) {
//这里index=size是合理的,因为是添加.当时index=size的时候,实际上是在尾部添加节点
if (index < 0 || index > size) {
return;
}
//这里需要遍历到目标index的前一个节点,所以不能等于index
ListNode temp = head;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
temp.next = new ListNode(val, temp.next);
this.size++;
}
public void deleteAtIndex(int index) {
//这里index也不能等于size,因为索引从零开始算。这里放过去下面会报空指针异常。
if (index < 0 || index >= size) {
return;
}
ListNode temp = head;
//这里需要遍历到目标index的前一个节点,所以不能等于index
for (int i = 0; i < index; i++) {
temp = temp.next;
}
temp.next = temp.next.next;
this.size--;
}
//双向链表
public class MyLinkedListDouble {
int size;
ListNodeDouble head;
ListNodeDouble tail;
MyLinkedListDouble(){
size=0;
head=new ListNodeDouble();
tail=new ListNodeDouble();
head.next=tail;
tail.pre=head;
}
public int get(int index) {
//这里也要注意,下标从0开始,所以当index=size的时候实际上也是无效的下标。
if (index < 0 || index >= size) {
return -1;
}
ListNodeDouble temp ;
//判断哪边遍历时间更短
if(index>=size/2){
temp=tail;
for (int i=size;i>index;i--){
temp=temp.pre;
}
}else {
temp=head;
for (int i = 0; i <= index; i++) {
temp = temp.next;
}
}
return temp.val;
}
public void addAtHead(int val) {
addAtIndex(0, val);
}
public void addAtTail(int val) {
addAtIndex(size, val);
}
public void addAtIndex(int index, int val) {
//这里index=size是合理的,因为是添加.当时index=size的时候,实际上是在尾部添加节点
if (index < 0 || index > size) {
return;
}
ListNodeDouble temp=head;
//这里需要遍历到目标index的当前节点,所以需要等于index
for (int i=0;i<=index;i++){
temp=temp.next;
}
ListNodeDouble newNode=new ListNodeDouble(val);
newNode.next=temp;
newNode.pre=temp.pre;
temp.pre.next=newNode;
temp.pre=newNode;
size++;
}
public void deleteAtIndex(int index) {
//这里index也不能等于size,因为索引从零开始算。这里放过去下面会报空指针异常。
if (index < 0 || index >= size) {
return;
}
ListNodeDouble temp=head;
//这里需要遍历到目标index的当前节点,所以需要等于index
for (int i = 0; i <= index; i++) {
temp=temp.next;
}
temp.pre.next=temp.next;
temp.next.pre=temp.pre;
size--;
}
Leecode 206 翻转链表
思路:
双指针思想很容易做出来。
代码:
public ListNode reverseList(ListNode head) {
if(head==null){return null;}
ListNode fast=new ListNode();
fast=head.next;
head.next=null;
while(fast!=null){
ListNode next=fast.next;
fast.next=head;
head=fast;
fast=next;
}
return head;
}