
重点:链表的操作一定要画图!!!
链表的操作一定要画图!!!
链表的操作一定要画图!!!
203.移除链表元素
题意:删除链表中等于给定值 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 输出:[]
思路:
对链表进行遍历,找到目标,进行删除
代码1:
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{
//指针向后移动进行遍历,head不变
curr = curr.next;
}
}
//返回head不是curr,此时curr已经指向最后一个节点
return head;
}
代码2:采用虚拟头节点,后续链表的操作基本都会采用这种方式
public ListNode removeElements(ListNode head, int val) {
//采用虚拟头节点的方式,就不用单独处理头节点
ListNode vir = new ListNode();
vir.next = head;
//定义一个指针,进行遍历
ListNode curr = vir;
while( curr.next != null){
if(curr.next.val == val){
curr.next = curr.next.next;
}else{
//指针向后移动进行遍历,head不变
curr = curr.next;
}
}
//返回vir.next不是head,因为存在head有可能被删除的情况
return vir.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的节点。
示例: 输入 ["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"] [[], [1], [3], [1, 2], [1], [1], [1]] 输出 [null, null, null, null, 2, null, 3] 解释 MyLinkedList myLinkedList = new MyLinkedList(); myLinkedList.addAtHead(1); myLinkedList.addAtTail(3); myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3 myLinkedList.get(1); // 返回 2 myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3 myLinkedList.get(1); // 返回 3
思路:
分别用单链表和双链表实现,双链表实现时,要特别注意代码的顺序,以免踩坑。
代码1,单链表实现
//单向链表
class MyLinkedList {
class ListNode{
int val;
ListNode next;
ListNode(){}
ListNode(int val){
this.val = val;
}
}
int size;
//虚拟头节点
ListNode head;
MyLinkedList(){
size = 0;
head = new ListNode(0);
}
public int get(int index) {
if(index >= size || index < 0){
return -1;
}
ListNode curr = head;
//当前指针指向虚拟头节点,因此遍历的时候要index
for(int i=0;i<=index;i++){
curr = curr.next;
}
return curr.val;
}
public void addAtHead(int val) {
ListNode newNode = new ListNode(val);
newNode.next = head.next;
head.next = newNode;
//不要忘记size++
size++;
}
public void addAtTail(int val) {
ListNode curr = head;
while(curr.next != null){
curr = curr.next;
}
ListNode tailNode = new ListNode(val);
curr.next = tailNode;
size++;
}
public void addAtIndex(int index, int val) {
if(index < 0 || index > size){
return;
}
ListNode newNode = new ListNode(val);
ListNode curr = head;
for(int i=0;i<index;i++){
curr = curr.next;
}
newNode.next = curr.next;
curr.next = newNode;
size++;
}
public void deleteAtIndex(int index) {
if(index < 0 || index >= size){
return;
}
size--;
ListNode curr = head;
for(int i=0;i<index;i++){
curr = curr.next;
}
curr.next = curr.next.next;
}
}
代码2:双链表
class MyLinkedList {
class ListNode{
int val;
ListNode next, prev;
ListNode(){}
ListNode(int val){
this.val = val;
}
}
int size;
//虚拟头节点,尾节点
ListNode head, tail;
MyLinkedList(){
size = 0;
this.head = new ListNode(0);
this.tail = new ListNode(0);
head.next = tail;
tail.prev = head;
}
public int get(int index) {
if(index >= size || index < 0){
return -1;
}
//从前往后更快
if(index <= size/2 - 1){
ListNode pCurr = head;
for(int i=0;i<=index;i++){
pCurr = pCurr.next;
}
return pCurr.val;
}else{
ListNode tCurr = tail;
for(int i=0;i<size-index;i++){
tCurr = tCurr.prev;
}
return tCurr.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;
}
ListNode newNode = new ListNode(val);
ListNode curr = head;
for(int i=0;i<index;i++){
curr = curr.next;
}
newNode.next = curr.next;
curr.next.prev = newNode;
newNode.prev = curr;
curr.next = newNode;
size++;
}
public void deleteAtIndex(int index) {
if(index < 0 || index >= size){
return;
}
size--;
ListNode curr = head;
for(int i=0;i<index;i++){
curr = curr.next;
}
//踩坑,下面2行不能交换位置,如果先将curr.next改变后,curr.next.next.prev就是根据变化后来进行计算了
curr.next.next.prev = curr;
curr.next = curr.next.next;
}
}
206.反转链表
题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
思路:
可以采用双指针法或者递归法实现
代码1:双指针法
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while(curr != null){
ListNode tmp = curr.next;
curr.next = prev;
prev = curr;
curr = tmp;
}
return prev;
}
代码2:递归法
public ListNode reverseList(ListNode head) {
return reverse(null,head);
}
private ListNode reverse(ListNode prev, ListNode curr){
if(curr == null){
return prev;
}
ListNode tmp = curr.next;
curr.next = prev;// 反转
return reverse(curr, tmp);
}
}
总结:今天进入新的数据结构,链表的学习,今天的题目本身并不难,但是需要特别细心,特别是在操作双链表的时候,一定要确定好代码的先后顺序,并使用临时变量保存后续要用到的数据,另外一定要画图,这样才能写出正确代码。
411

被折叠的 条评论
为什么被折叠?



