王道数据结构课后习题 | P37

3、设L为带头节点的单链表,编写算法实现从尾到头反向输出每个节点的值。
我想到的是reverse一下/笑哭
想想这个就有点杀鸡用牛刀的感觉…
看了题解说是可以用递归…卧槽瞬间orz

class Solution{
public:
    //利用递归解决 
    void reverseOut(ListNode* head){
        if(head->next!=NULL)
        reverseOut(head->next);
        printf("%d ",head->val);
    }

    //利用reverse将整个链表倒置 
    void reverseOutput(ListNode *head){
        ListNode root(-1);
        root.next=head;
        //获得尾节点
        ListNode* end=head;
        while(end->next!=NULL) end=end->next;

        //整个链表反转过来 
        reverse(&root,head,end);

        outputNode(head);
    }

    ListNode* reverse(ListNode* prev,ListNode* begin,ListNode *end){

        ListNode* end_next=end->next;
        for(ListNode *p=prev,*cur=begin,*next=cur->next;
        cur!=end_next;
        p=cur,cur=next,next=next==NULL?NULL:next->next) {
            cur->next=p;
        }
        begin->next=end_next;
        prev->next=end;
        return begin;
    }
}; 

8、给定两个单链表,找出两个链表的公共节点。
这道题我想到的办法是用哈希,以unordered_map对指针进行存储,这样遍历第一个链表。
再对第二个链表进行遍历,并判断每个指针在里面是否存在。
如果存在,推入一个vector里面

讲道理效率应该还不错O(n+m),如果不允许用额外存储空间呢?

我们可以确信,二者相遇之后,肯定是相同的节点一直到end,为什么呢?
因为链表只有唯一的next域
Y链表
于是我们可以算出两个链表路程差距是多少,然后让其中一头先走这么多个差距

然后再一起向前走,ok那样就会同时到达共同节点
复杂度O(m+n)


class Solution{
public:
    //技巧
    ListNode* getSameNodes(ListNode* head1,ListNode* head2) {
        int len1=0,len2=0;
        for(ListNode* temp=head1;temp;temp=temp->next) len1++; 
        for(ListNode* temp=head2;temp;temp=temp->next) len2++; 
        //获取到两个长度之后,要取得差值,并将差值对应的项目向前移动xxx 
        ListNode* cur1,*cur2; 
        if(len1>len2){
            //先走k步,让二者可以在同一点相遇 
            int k=len1-len2;
            while(k--) len1=len1->next;
        }
        else{
            int k=len2-len1;
            while(k--) len2=len2->next;
        }

        while(len1){
            if(len1==len2) return len1;
            else{
                len1=len1->next;
                len2=len2->next;
            }
        }
        return null; 
    }

    //使用哈希 
    vector<ListNode*> getSameNodes(ListNode* head1,ListNode* head2){
        vector<ListNode*> result;
        unordered_map<ListNode*,bool> existed;
        for(ListNode* temp=head1;temp!=NULL;temp=temp->next) {
            existed[temp]=true;
        }
        for(ListNode* temp=head2;temp!=NULL;temp=temp->next) {
            if(existed.find(temp)!=existed.end()){
                result.push_back(temp);
            }
        }
        return result;
    }
};

13、合并两个递增的链表,要求合并之后的链表递减。
这道题很显然是归并的思路,然后借鉴了LeetCode上 add two number的思路,
使判断结束条件变为cur1|| cur2.这样在一个链表长度大于另一个的时候,使用辅助变量max来帮助计算。
从而简化代码

class Solution{
public:
    //将两个递增链表合并,要求合并后链表递减 
    ListNode* mergeReverse(ListNode* head1,ListNode* head2) {
        ListNode root(-1),root1(-1),root2(-1);
        ListNode* head=&root;
        int max=999999;
        for(ListNode* prev1=&root1,*prev2=&root2,*cur1=head1,*cur2=head2;
            cur1 || cur2;){

            int v1=cur1==NULL?max:cur1->val;
            int v2=cur2==NULL?max:cur2->val;

            if(v1<v2) {
                //取head1链表
                ListNode* next=cur1->next;

                cur1->next=head->next;
                head->next=cur1;
                cur1=next;
            }
            else{
                //取head2
                ListNode* next=cur2->next;
                cur2->next=head->next;
                head->next=cur2;
                cur2=next; 
            }
        }
        return root.next;
    }
}; 

16、判断一个序列是否是另一序列的子序列
用的很朴素的办法,不过还行

class Solution{
public:
    //判断head2链表是否是head1链表的子链 
    bool IsSubSequence(ListNode* head1,ListNode* head2) {
        for(ListNode *cur1=head1;cur1;cur1=cur1->next){
            int v1=cur1->val;
            ListNode *cur2=head2,*temp=cur1;

            while(temp && cur2 && temp->val==cur2->val){
                temp=temp->next;cur2=cur2->next;
            }
            //没有找到 
            if(cur2!=NULL) continue;
            return true;
        }
        return false;
    }
};

17、判断循环双链表是否对称
算法没有什么好说的,有些地方需要关心一下,具体详见注释
主要有:1、struct定义
2、堆栈变量和内存变量的区别

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h> 
#include<string.h>
#include<stdlib.h>
#include<vector>
using namespace std;

struct ListNode {
    int val;
    ListNode *prior,*next;
    //这里是c++的一种初始化方法,利用 变量名(变量值) 来进行初始化
    //在{}内再进行相关的操作,即初始化与操作分离 
    ListNode(int x) : val(x),prior(NULL),next(NULL) {}
};

ListNode* initNode(vector<int> arr){
    //注意这里的root
    //如果申明为 ListNode root(-1),这里的root将是一个堆栈变量
    //在函数执行结束之后,将会被释放
    //于是将一个堆栈变量的指针传回,它将变成一个无意义的指针
    //所以需要使用new,将其申明为内存变量 
    ListNode* root=new ListNode(-1);
    ListNode* end = root;
    int n = arr.size();
    for (int i = 0; i < n; i++){
        end->next = new ListNode(arr[i]);
        end->next->prior=end;
        end = end->next;
    }
    end->next=root;
    root->prior=end;

    return root;
}

void outNode(ListNode* root){
    //带头节点的双链表
    ListNode* head=root->next;
    while(head && head!=root){
        printf("%d ",head->val);
        head=head->next;
    }
    printf("\n");
}

class Solution{
public:
    //root是头节点 
    bool IsSymmetry(ListNode *root){
        for(ListNode* p=root->prior,*q=root->next;p!=q;p=p->prior,q=q->next){
            if(p->val!=q->val){
                return false;
            }
        }
        return true;
    }
};

19、双链表元素删除x
链表的删除有一点需要注意,如果需要删除x元素,不需要遍历两遍。
只需要在第一遍遍历的时候使用双指针,另一个指针叫做minp,保存最小节点的前驱节点。
这样在一轮循环之后,可以在O(1)的时间内,利用这个前驱删除相应的节点

21、找出倒数第k个元素
使用两个指针,第一个指针先走k步,然后两个指针一起走,直到第一个指针走到末尾。
然后就是在向前走k的时候注意指针判空这些小细节

class Solution{
public:
    int get_kth_back(ListNode* head,int k){
        ListNode* pre=head,*cur=head;
        for(int i=1;i<k && cur;i++) cur=cur->next;
        if(!cur) return -1;

        for(;cur->next;cur=cur->next,pre=pre->next);

        if(pre) return pre->val;
        else return -1;
    }
}; 

23、删除重复节点
没什么好说的,主要想说一下unordered_map头文件引用
一般应该#include< unordered_map >就行了,但是我的devc不过
于是,应当是

#if(__cplusplus == 201103L)
#include <unordered_map>
#include <unordered_set>
#else
#include <tr1/unordered_map>
#include <tr1/unordered_set>
namespace std
{
    using std::tr1::unordered_map;
    using std::tr1::unordered_set;
}
#endif

思考题:找出一个数组里大小之和为k的
重新回顾一下快排

int Partition(int a[],int left,int right){
    int x=a[left];
    while(left<right){
        while(left<right && a[right]>x) right--;
        if(left<right) a[left]=a[right];
        while(left<right && a[left]<x) left++;
        if(left<right) a[right]=a[left];
    }
    a[left]=x;
    return left;
}

void QSort(int a[],int left,int right){
    if(left<right){
        int mid=Partition(a,left,right);
        QSort(a,0,mid-1);
        QSort(a,mid+1,right);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值