代码随想录训练营第三天| 203.移除链表元素、707.设计链表、206.反转链表

博客围绕链表操作展开,解析了三道题目。移除链表元素使用虚拟头指针使操作逻辑一致;设计链表采用虚拟头结点单向链表,要注意空指针判断和内存释放;反转链表可用双指针或递归方法,递归是将指针更新工作交给递归处理。

 203.移除链表元素

题目链接/文章讲解/视频讲解:: 代码随想录
ListNode* removeElements(ListNode* head, int val) {
        ListNode* sentinel = new ListNode(-1,head);
        ListNode* p1 = sentinel;
        while(p1->next != nullptr) {
            if (p1->next->val != val)
            p1 = p1->next;
            else {
                p1->next = p1->next->next;
            }
        }
    return sentinel->next;
    }

 该题比较基础,最重要的思路就是使用虚拟头指针指向原链表的头结点,这样可以使得整个移除操作逻辑一致,不用单独为头结点进行操作,并且在返回时也不用考虑原始头结点被移除的情况。

707.设计链表

题目链接/文章讲解/视频讲解: 代码随想录
class MyLinkedList {
public:
    // 定义链表节点结构体
    struct LinkedNode {
        int val;
        LinkedNode* next;
        LinkedNode(int val):val(val), next(nullptr){}
    };

    // 初始化链表
    MyLinkedList() {
        _dummyHead = new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
        _size = 0;
    }
    
    int get(int index) {
        if (index > _size-1 || index < 0)
            return -1;
        else {
            LinkedNode* p1 = _dummyHead;
            for (int i = 0; i <= index; i++){
                p1 = p1->next;
            }
            return p1->val;
        }
    }
    
    void addAtHead(int val) {
        LinkedNode* newNode = new LinkedNode(val);
        if (_dummyHead->next != nullptr) {
            newNode->next = _dummyHead->next;
            _dummyHead->next = newNode;
        }
        else {
            _dummyHead->next = newNode;
        }
        _size++;

    }
    
    void addAtTail(int val) {
        LinkedNode* newNode = new LinkedNode(val);
        LinkedNode* p1 = _dummyHead;
        while (p1->next != nullptr)
            p1 = p1->next;
        p1->next = newNode;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
        if (index > _size || index < 0)
            return;
        else {
            LinkedNode* p1 = _dummyHead;
            for (int i = 0; i < index; i++) {
                p1 = p1->next;
            }
            LinkedNode* newNode = new LinkedNode(val);
            newNode->next = p1->next;
            p1->next = newNode;
            _size++;
        }
    }
    
    void deleteAtIndex(int index) {
        if (index >= _size || index < 0)
            return;
        else {
            LinkedNode* p1 = _dummyHead;
            for (int i = 0; i < index; i++) {
                p1 = p1->next;
            }
            if (p1->next->next != nullptr)
                p1->next = p1->next->next;
            else
                p1->next = nullptr;
            _size--;
        }
    }
private:
    int _size;
    LinkedNode* _dummyHead;
};

这题是一道较为综合的题目, 考虑了链表的各种操作,从初始化到插入和删除。我采用了虚拟头结点单向链表做法,要注意的就是注意判断空指针,不要让程序访问到空指针内,以免造成未知的错误。在遍历链表的时候应该创建一个新的结点指向虚拟头结点再进行遍历,这样就不会丢失原有的头结点。

示例代码中采用了如下代码来遍历链表,较为简洁:

while(index--) {
            cur = cur ->next;
        }

同时在删除链表节点时,我并没有释放内存空间,但在C++中删除节点后应该释放内存空间已保证程序的安全性。示例代码如下:

LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
//delete命令指示释放了tmp指针原本所指的那部分内存,
//被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
//如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
//如果之后的程序不小心使用了tmp,会指向难以预想的内存空间

 206.反转链表 

题目链接/文章讲解/视频讲解: 代码随想录
ListNode* reverseList(ListNode* head) {
        ListNode* temp; // 保存cur的下一个节点
        ListNode* cur = head;
        ListNode* pre = NULL;
        while (cur) {
            temp = cur->next;  // 保存一下 cur的下一个节点,因为接下来要改变cur->next
            cur->next = pre; // 翻转操作
            // 更新pre 和 cur指针
            pre = cur;
            cur = temp;
        }
        return pre;
    }

 使用双指针方法,cur指针指向当前节点,pre指针指向当前节点的前一个节点,也就是当前节点要将next转向的那个节点,然后使用temp来记录cur该指向的下一个结点。

class Solution {
public:
    ListNode* reverse(ListNode* pre,ListNode* cur){
        if(cur == NULL) return pre;
        ListNode* temp = cur->next;
        cur->next = pre;
        // 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
        // pre = cur;
        // cur = temp;
        return reverse(cur,temp);
    }
    ListNode* reverseList(ListNode* head) {
        // 和双指针法初始化是一样的逻辑
        // ListNode* cur = head;
        // ListNode* pre = NULL;
        return reverse(NULL, head);
    }

};

上面是示例代码的递归方法,思路和双指针方式其实是一样的,只不过将更新pre和cur指针的工作交给了递归来处理,而不是利用循环来解决。

由于提供的引用中未涉及Java实现移除链表元素时main方法中的输入处理的相关内容,下面是一个示例代码展示该场景下main方法的输入处理方式。此示例假设输入是一系列整数表示链表元素,以及一个整数表示要移除元素。 ```java import java.util.Scanner; // 定义链表节点类 class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class RemoveLinkedListElements { public static ListNode removeElements(ListNode head, int val) { // 创建一个虚拟头节点 ListNode dummy = new ListNode(0); dummy.next = head; ListNode prev = dummy; ListNode curr = head; while (curr != null) { if (curr.val == val) { prev.next = curr.next; } else { prev = curr; } curr = curr.next; } return dummy.next; } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // 读取链表元素,假设输入以空格分隔 System.out.println("请输入链表元素,以空格分隔:"); String[] input = scanner.nextLine().split(" "); ListNode dummy = new ListNode(0); ListNode current = dummy; for (String num : input) { current.next = new ListNode(Integer.parseInt(num)); current = current.next; } ListNode head = dummy.next; // 读取要移除元素 System.out.println("请输入要移除元素:"); int valToRemove = scanner.nextInt(); // 移除元素 ListNode newHead = removeElements(head, valToRemove); // 输出移除元素后的链表 System.out.println("移除元素后的链表:"); current = newHead; while (current != null) { System.out.print(current.val + " "); current = current.next; } scanner.close(); } } ``` 在上述代码中,`main` 方法使用 `Scanner` 类从控制台读取用户输入。首先读取一系列以空格分隔的整数作为链表元素,然后读取一个整数作为要移除元素。接着调用 `removeElements` 方法移除指定元素,并输出移除元素后的链表
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值