链表中的一些技巧

1.查找链表的等分结点(快慢指针):

1.1描述:

给定一个链表查找中间结点、第一个三等分点。。。。

1.2方法:

法一:先遍历一遍链表,使用变量i记录结点个数m,再遍历链表,每遍历一个结点,则i++,直到i==m/n;
法二:使用快慢指针,慢指针走一步,则快指针走2步或3步。。。,直到快指针不满足循环体的循环条件(循环条件很重要,并且还要分偶数链表还是奇数链表)

1.3代码实现:

仅针对法二,查找中间结点

ListNode* first_half_end(ListNode* head){
        ListNode *slow,*fast;
        slow=head;
        fast=head;
        while(fast->next!=NULL && fast->next->next!=NULL  ){//前一个是偶数链表情况 后一个是奇数链表情况
            slow=slow->next;
            fast=fast->next->next;
        }  
        return slow;
    }

1.4应用:

leetcode 234.回文链表 法三

2.将链表转化为数组:

2.1描述:

链表是顺序访问,无法使用两个指针实现相向访问,但是数据可以随机访问,因此可将链表转化为数组来处理。

2.2方法:

再C++中,使用vector容器来作为数组,每访问一个链表中的结点,就用emplace_back()方法,将结点放入vector容器中。

2.3代码实现:

		 vector<int> vals;

         while(head!=nullptr){

            vals.emplace_back(head->val);
            head=head->next;

        }

2.3应用:

leetcode 234.回文链表 法一

3.加法运算中的进制:

3.1描述:

通过计算机实现加法运算,需要注意进制。

3.2方法:

1.使用变量carry记录每位相加时产生的进位。进位的计算:carry = sum/10;
2. 每位相加后的数的计算:value=sum%10;
3. 注意:最高位的进位在循环结束后处理;

3.3代码实现:

	class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        
        ListNode *l3=new ListNode; 
/*小技巧:对于链表问题,返回结果为首元结点时,通常需要先初始化头结点head;,
该指针的下一个节点指向真正的首元结点。
使用头结点的目的在于链表初始化时无可用节点值,而且链表构造过程需要指针移动,
进而会导致头指针丢失,无法返回结果。并且使用头结点能够使得循环中的代码保持一致。
*/
        ListNode *p;
        p=l3;
        int x,y,sum=0;
        int carry=0;
        int val3; //记录每一位相加后的值

        while(l1!=nullptr || l2!=nullptr ){
            
            x=(l1==nullptr ? 0:l1->val);
            y=(l2==nullptr ? 0:l2->val);


            sum=x+y+carry;
            val3=sum%10;
            carry=sum/10;

            
            p->next=new ListNode(val3);
            p=p->next;

            if(l1!=nullptr)
                l1=l1->next;
            
            if(l2!=nullptr)
                l2=l2->next;
            
        }
        //99999+999;最高位还要进一位
        if(carry==1){
           
            p->next=new ListNode(1);;
        }

        return l3->next;

    }
};

3.4应用:

leetcode 2.两数相加。

4.哨兵结点(dummy node):

4.1使用情况:

在很多时候,我们处理某个节点需要用到它的前驱节点,比如删除链表的某个节点 ,对于没有哨兵的单链表,当待删除的节点为链表的第一个节点, 由于没有前驱,需要进行特殊处理,从而代码的复杂性增加.而如有哨兵节点,则第一个节点的处理方式与其他节点相同,可以统一进行处理.
对于链表的处理,我们经常在头结点之前设置一个哑结点dummyHead 。我们将这个结点指向头结点。
为什么这么做?
因为在删除链表元素的时候,说不定要删除的元素中包含头结点。如果我们从头结点开始,就要把自己所站的位置删除。这就挺尴尬的了…(内心独白:让我来删除结点,结果删除的却是我自己o(╥﹏╥)o…)

	为了避免这样的情况,我们设置一个哑结点,指向头结点。这样虽然浪费一点空间,但换来了大大的好处,至少不用分类讨论头结点的问题

5. 链表中的栈:

5.1 使用情况:

链表只能从头结点开始往后遍历查找结点,若是能够像数组那样从后往前查找结点,则会方便很多。通过栈能够实现这种想法。

5.2 方法:

使用STL中的stack容器作为栈。遍历链表,将结点的地址(而不是结点的值)放入stack中。

5.3 应用与代码实现:

leetcode 19.删除链表中倒数第N个结点

/**
 * Definition for singly-linked list.
 * 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) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
//用到前一个指针pre ,创建哨兵结点
        ListNode* dummy=new ListNode(0,head);

        ListNode* curr=dummy;
        ListNode* pre;
        stack<ListNode*> stack1;
//将结点的地址放入stack容器中
        while(curr!=nullptr){

            stack1.push(curr);
            curr=curr->next;

        }
//弹出后n个结点
        for(int i=0;i<n;i++){

            stack1.pop();

        }
//倒数第n个结点的前一个结点地址
        pre=stack1.top();
        pre->next=pre->next->next;

//返回哨兵结点的后一个结点
        return dummy->next;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值