0
点赞
收藏
分享

微信扫一扫

消息传递神经网络(Message Passing Neural Networks, MPNN)

才德的女子 2024-12-02 阅读 9

题目引用


  1. 移除链表元素
  2. 设计链表
  3. 翻转链表

链表介绍


链表与数组不同的是,它是以指针串联在一起的分布在内存随机位置上的,而不是像指针一样占用整块的连续空间。因此也不支持通过指针++读取。所以在题目里面总是比较抽象,需要通过画图来帮助解题。一般出现在算法里面的都会是单链表,结构形如

struct ListNode {
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
   	ListNode(int x) : val(x), next(nullptr) {}
   	ListNode(int x, ListNode *next) : val(x), next(next) {}

1.移除链表元素


来看一下题目~
题目要求我们删除链表中 ==val 的元素,因此我们需要遍历一遍数组将 ==val 的节点删掉,怎么删呢?
首先定义一个cur指针,当cur指针所在节点的下一个节点的值 ==val 时,我们使用一个tmp指针指向它,然后 cur指针指向的节点将 next 指向 tmp 的下一个节点,完成删除。
初始化
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最后附上代码:

 ListNode* removeElements(ListNode* head, int val) {
        while(head!=NULL&&head->val==val){
            ListNode*tmp=head;
            head=head->next;
            delete tmp;
        }

        ListNode* cur=head;
        while(cur!=NULL && cur->next!=NULL){
            if(cur->next->val==val){
                ListNode* tmp=cur->next;
                cur->next=cur->next->next;
                delete tmp;
            }else{
                cur=cur->next;
            }
        }

        return head;
    }

这里值得注意的是头结点位置的值,如果头结点的值 ==val 的话,需要我们直接从头结点删除,并把头结点指针移动到下一个位置。

设计链表


这里我们就选择难度较大的单链表来实现吧
首先需要自己定义一个链表节点,这个在文章开头就有,直接copy~
然后在类内初始化链表长度_size和一个虚拟头指针dummyhead
首先是构造函数,在构造函数内给_sizedummyhead赋值和开辟空间

MyLinkedList() {
        _size=0;
        dummyHead=new ListNode(0);        
    }

然后是get函数,返回index位置的值,这里需要判断index位置是否越界,接着遍历链表找到index位置的节点并返回val

int get(int index) {
        if(index<0||index>(_size-1))
            return -1;
        ListNode* cur=dummyHead->next;
        while(index--){
            cur=cur->next;
        }
        return cur->_val;
    }

头插函数,这里new一个新节点,将新节点的next指针指向dummyhead的下一个节点,dummyheadnext指向新节点,别忘了++_size

 void addAtHead(int val) {
        ListNode* newNode=new ListNode(val);
        newNode->next=dummyHead->next;
        dummyHead->next=newNode;
        _size++;
    }

尾插,一路循环到链表的尾节点,将尾节点的next指针指向新节点

void addAtTail(int val) {
        ListNode* newNode=new ListNode(val);
        ListNode*cur=dummyHead;
        while(cur->next!=nullptr){
            cur=cur->next;
        }
        
        cur->next=newNode;
        _size++;
    }

接下来的插入删除指定位置函数都比较简单,就直接上完整代码吧

class MyLinkedList {
public:
    struct ListNode{
    struct ListNode* next;
    int _val;
    ListNode(int val):_val(val),next(nullptr){}
    };

    MyLinkedList() {
        _size=0;
        dummyHead=new ListNode(0);        
    }
    
    int get(int index) {
        if(index<0||index>(_size-1))
            return -1;
        ListNode* cur=dummyHead->next;
        while(index--){
            cur=cur->next;
        }
        return cur->_val;
    }
    
    void addAtHead(int val) {
        ListNode* newNode=new ListNode(val);
        newNode->next=dummyHead->next;
        dummyHead->next=newNode;
        _size++;
    }
    
    void addAtTail(int val) {
        ListNode* newNode=new ListNode(val);
        ListNode*cur=dummyHead;
        while(cur->next!=nullptr){
            cur=cur->next;
        }
        
        cur->next=newNode;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
        if(index>_size)
            return;
        if(index<0) index=0;
        ListNode* newNode=new ListNode(val);
        ListNode* cur=dummyHead;
        while(index--){
            cur=cur->next;
        }
        
        newNode->next=cur->next;
        cur->next=newNode;
        _size++;
    }
    
    void deleteAtIndex(int index) {
        if(index<0||(index>=_size))
            return;
        ListNode* cur=dummyHead;
        while(index--){
            cur=cur->next;
        }
        ListNode* tmp=cur->next;
        cur->next=cur->next->next;
        delete tmp;
        _size--;
    }
private:
    int _size;
    ListNode* dummyHead;
};

反转链表


我们来看一下题目,让我们将链表翻转之后返回头节点的指针。
这里有两种办法:一种从前往后遍历数组,一边将当前节点next指针指向下一个,一边向前遍历,最后返回当前节点的指针。
另一种通过递归直接来到倒数第二个节点位置,将尾节点的next的指针指向自己,再返回上一层调用。
我们两种都讲
第一种
我们定义cur指针指向headpre指针用于指向cur的前一个节点,再定义一个tmp用于记录cur指针的下一个位置
在这里插入图片描述
只要cur指针指向的节点存在,就使tmp指向cur指针的下一个节点,并把curnext指针指向pre所在位置,再将pre移动到cur位置cur移动到tmp位置,三个指针一次循环同时向前移动一次直到循环结束。
在这里插入图片描述
在这里插入图片描述
这里就附上代码供大家理解一下:

ListNode* reverseList(ListNode* head) {
        ListNode* pre=NULL; 
        ListNode* cur=head;
        ListNode* tmp;
        while(cur){
            tmp=cur->next;
            cur->next=pre;
            pre=cur;
            cur=tmp;
        }

        return pre;
    }

第二种写法:
我们利用递归找到倒数第二个节点,利用倒数第二个节点将尾节点的next指针指向自己,然后返回上一层递归,不断调用直到所有节点的next指针都被翻转,返回接收的头指针。来看一下图。
在这里插入图片描述
附上代码:

ListNode* reverseList(ListNode* head) {
        // 边缘条件判断
        if(head == NULL) return NULL;
        if (head->next == NULL) return head;
        
        // 递归调用,翻转第二个节点开始往后的链表
        ListNode *last = reverseList(head->next);
        // 翻转头节点与第二个节点的指向
        head->next->next = head;
        // 此时的 head 节点为尾节点,next 需要指向 NULL
        head->next = NULL;
        return last;
    }

总结


在遇到链表问题时,初学者切忌空想,一定要多画图,多思考,当你做出来一道时,就离做出一百道不远啦~

举报

相关推荐

0 条评论