C++ OJ刷题记录(LintCode)

Tips:++i 的执行速度比i++快,与之对应的另一个单元运算符也是如此

链表部分

简单难度

1. 链表求和

题目详情

描述:
你有两个用链表代表的整数,其中每个节点包含一个数字。数字存储按照在原来整数中相反的顺序,使得第一个数字位于链表的开头。写出一个函数将两个整数相加,用链表形式返回和。

样例:
输入: 7->1->6->null, 5->9->2->null
输出: 2->1->9->null
样例解释: 617 + 295 = 912, 912 转换成链表: 2->1->9->null

方法:
第一步:新建一个链表,使用尾插法建表
第二步:将两个链表对应位置依次相加,并用一个变量来保存相加结果同时利用这个变量来判断是否有进位。假设该变量为a,a%10即为对应位置的数值,a/10即可得到是否有进位。
第三步:把数插进链表中
第四步:判断两个提供数据的链表是否为空以及是否有进位,只要有一个链表不为空或者有进位则继续进行上述操作。

演示:
243+564=807

2 4 3
5 6 4
进行逐位相加
7 10 7
这时候第二位有进位(根据常识,某一位上的数只能是0-9)
进行%10操作,得到第二位的数值并存入新链表中,再进行/10操作,得到进位
这样进行下一轮计算的时候,a就不是0了,而是1。这样就可以达到将最后一位修正的目的。
最终结果
7 0 8

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param l1: the first list
     * @param l2: the second list
     * @return: the sum list of l1 and l2 
     */
     ListNode *head = new ListNode;
    ListNode * addLists(ListNode * l1, ListNode * l2) {
        ListNode *head = new ListNode(0);//建立头结点
        ListNode *p = head;//头指针
        int carry = 0;//保存加法结果,并作为判断是否有进位的依据
        while(true) {
            if(l1!=nullptr){//只要l1不为空就进行
                carry+=l1->val;
                l1=l1->next;
            }
            if(l2!=nullptr) {//只要l2不为空就进行
                carry+=l2-> val;
                l2=l2->next;
            }
            p->val=carry%10;//将相加后得到的数放入新建的链表中
            carry/=10;//获得仅为信息
            
            if(l1!=nullptr||l2!=nullptr||carry!=0) {//只要两链表有一个不为空或者有进位继续进行循环
                p=(p->next=new ListNode(0));//分配新的节点
            } else//l1,l2全为空并且没有进位循环结束
            	break;
        }
        
        return head;//一定要返回,否则OJ得到的结果永远为NULL或提示你segement fault
    }
};

2. 翻转链表

题目详情

描述:
翻转一个链表

样例:
输入: 1->2->3->null
输出: 3->2->1->null

方法:
第一步:新建一个链表结点(声明为nullptr)用来存储从原链表上摘下来的结点(倒过来的尾插法)。
第二步:进行摘结点的操作:
① 在循环体中创建一个临时结点,用于存储当前结点的下一个结点避免结点丢失。
② 让当前结点(也就是头指针指向的结点)的后继结点指向第一步新建的结点。
③ 让新建的链表的表指针指向新插入的结点
④ 把原链表的头指针还回去,让它指向临时结点指向的下一个节点。
第三步:返回新生成的链表

AC代码:

/**
 * Definition of singly-linked-list:
 *
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head: n
     * @return: The new head of reversed linked list.
     */
    ListNode * reverse(ListNode * head) {
       ListNode *pre = nullptr;//用于存储摘下来的结点
        
        while(head!=nullptr){
            ListNode *temp = head->next;//用于存储头结点的下一个结点,防止丢失
            head->next = pre;//让头结点的后继指向新建的结点
            pre=head;//让新建链表的指针指向新插入结点
            head=temp;//归还头指针
        }
        
         return pre;
    }
    
   
};

3. 删除排序链表中的重复元素

题目详情

描述:
给定一个排序链表,删除所有重复的元素每个元素只留下一个。

样例:
样例 1:
输入: null
输出: null

样例 2:
输入: 1->1->2->null
输出: 1->2->null

样例 3:
输入: 1->1->2->3->3->null
输出: 1->2->3->null

方法:
只需要将当前结点的值与下一结点的值进行比较,如果相同,则将下一结点跳过,否则将指针移动到下一节点(注意开头判空)
注意循环条件一定是p->next不为空,因为要与下一结点进行比较,如果下一结点为空,则会提示segement fault。而且,最后一定要返回头结点,否则永远为NULL

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head: head is the head of the linked list
     * @return: head of linked list
     */
    ListNode * deleteDuplicates(ListNode * head) {
        if(head==nullptr) {
            return nullptr;
        }
        
        ListNode *p = head;//新建一指针用于循环,保持头指针不动
        while(p->next!=nullptr){
            if(p->val == p->next->val) {//进行比较,值相同跳过,不相同指向下一结点
                p->next = p->next->next;
            }else{
                p=p->next;
            }
            
        }
        
        return head;
    }
};


4. 链表化数组

题目详情

描述:
将一个数组变成链表

样例:
例1:

输入: [1,2,3,4],
输出: 1->2->3->4->null.

例2:

输入: [1,2],
输出: 1->2->null.

方法:
把数串起来就行。但要注意判空,以及这个是一个不带头结点的单链表。

AC代码:

/**
 * Definition of ListNode
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *         this->val = val;
 *         this->next = NULL;
 *     }
 * }
 */


class Solution {
public:
    /*
     * @param nums: an integer array
     * @return: the first node of linked list
     */
    ListNode * toLinkedList(vector<int> &nums) {
        if(nums.size() == 0) 
            return nullptr;
        
        ListNode *head = new ListNode(nums[0]);
        head->next = nullptr;
        ListNode *p = head;
        
        for (int i = 1; i < nums.size(); ++i) {
           p->next = new ListNode(nums[i]);
           p=p->next;
        }
        
       
        return head;
    }
};

5. 无序链表的重复项删除

题目详情

描述:
设计一种方法,从无序链表中删除重复项。

样例:
样例 1:

输入:1->2->1->3->3->5->6->3->null
输出:1->2->3->5->6->null

样例 2:

输入:2->2->2->2->2->null
输出:2->null

方法:
因为水平有限,只能想到一种最笨的方法。采用三指针,双重循环方案。

一开始先判空:判断第一个结点是否为空,第一个结点有没有后继结点。因为如果没有后继,那么将无法进行比较。

① 一个position指针指向当前需要被比较的结点,一个q指针指向position指针的下一个结点,一个p指针指向q的前驱结点。
② 当两相邻结点的值相等的时候,让q的前驱结点p的后继指向q后继结点,即p->next = q->next。同时注意释放此时q指针指向的结点。然后让q向后挪动一个位置。
③ 当两相邻接点不等的时候,将q向后挪动一个位置,同时让p指向q刚刚指向的结点。因为p始终指向q的前驱结点,
④ 比较完成后,让position后移一个位置。

最后,一定要返回头指针,不然OJ获得的结果永远为空

注意:
p和q一定要放在第一重循环中更新。不然会出现结点跳跃等诡异到让人难以理解(与预期完全不符)的结果

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head: The first node of linked list.
     * @return: Head node.
     */
    static ListNode *removeDuplicates(ListNode *head) {
        if (head == nullptr || head->next == nullptr)//判空,注意判空条件
            return head;//注意返回head,而不是无脑返回nullptr(可能导致错误)
            
        ListNode *position = head;//用于记录当前被比较的结点
        
        while (position!= nullptr) {
           ListNode *p=position;//指示q的前驱结点
           ListNode* q=position->next;//指向用于比较的结点
           while (q!= nullptr) {
               if(position->val == q->val) {//如果两结点的值相等
                   ListNode *temp = q;//定义一个临时结点存储当前p指向的结点
                   p->next = q->next;//把现在p指向的结点跳过
                   delete temp;//释放重复结点所占用的空间
               } else{
                  p = q;//因为两结点的值不相同,所以将q向后挪动一位,并同步将p向后挪动一位
               }   
               q = p->next;//让q指向p现在的后继结点,也就是下一个需要比较的结点
           }
            position = position->next;//将position指针后移一位
        }
        
        return head;
    }
};

6. 在排序链表中插入一个节点

题目详情
描述:
在链表中插入一个节点。

样例:
样例 1:

输入:head = 1->4->6->8->null, val = 5
输出:1->4->5->6->8->null

样例 2:

输入:head = 1->null, val = 2
输出:1->2->null

方法:
将要插入的值与链表中的值一次比较,找到合适的位置,插入。
① 判空。对原本为空的链表进行处理,即:新建一个结点用来存储要插入的值,然后让这个结点指向原本的头结点(也就是传入为nullptr的那个),这样新结点的后继就变成了nullptr。此时返回新结点即可得到结果。
② 考虑边界情况。插入的值小于等于所有链表中的值。那么直接进行一个判断,然后把新结点插入到最前边(与①的操作相同),然后返回新结点。
③ 考虑正常情况,要插入的值在链表的中间位置进行插入。先判断是否还有后继结点,因为如果要插入的值大于当前结点的值要向后迭代,如果后继结点为空那么继续进行迭代则会引发内存错误。然后判断当前值是否大于后继结点的值。因为边界情况(也就是直接插入第一个结点的情况)已经在②中解决了那么,所以第一个结点无需再次比较,比较它的后继结点即可。

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head: The head of linked list.
     * @param val: An integer.
     * @return: The head of new linked list.
     */
    ListNode * insertNode(ListNode * head, int val) {
        ListNode *q = new ListNode(val);
        q->next = nullptr;
        
        if(head == nullptr) {
           return q;
        }

        ListNode *p = head;
        ListNode *m = nullptr;
        if (p->val>= val) {
           q->next = head;
           return q;
        }
        
        while(p ->next != nullptr && val > p->next->val) {
            p=p->next;
        }
        
        q->next = p->next;
        p->next =q;
        
        return head;
    }
};

7. 删除链表中的元素

题目详情

描述:
删除链表中等于给定值 val 的所有节点。

样例

样例 1:

输入:head = 1->2->3->3->4->5->3->null, val = 3
输出:1->2->4->5->null

样例 2:

输入:head = 1->1->null, val = 1
输出:null

方法:
注意这个题是不带头结点的单链表,为了方便操作我们需要把它扩充为带头结点的单链表,这样可以有效解决第一个结点就相等的情况带来的麻烦。.

因为原题给的是用类定义的结点,所以声明方法如下:
ListNode listHead;
listHead.next = head;
head = &listHead;

剩下的就很简单了,只需要依次比较找出相同结点并将其从链表中去掉即可。

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head a ListNode
     * @param val an integer
     * @return a ListNode
     */
    ListNode *removeElements(ListNode *head, int val) {
        ListNode dummy;
        dummy.next = head;
        head = &dummy;
        
        while (head->next != nullptr) {
            if (head->next->val == val) {
                head->next = head->next->next;
            } else {
                head = head->next;
            }
        }
        
        return dummy.next;//在类中定义的时候next为指针型变量,此时next指向头结点,所以返回next;
    }
};

8. 合并两个排序链表

题目详情

描述:
将两个排序链表合并为一个新的排序链表。

样例

样例 1:

输入: list1 = null, list2 = 0->3->3->null
输出: 0->3->3->null

样例2:

输入:  list1 =  1->3->8->11->15->null, list2 = 2->null
输出: 1->2->3->8->11->15->null

方法:
创建一个新的带头结点的单链表用于存放结点,然后进行题目中给出的两链表的值比较。
如果l1<l2的值,那么把l1的当前结点插入新的单链表中,否则把l2的当前节点插入新的单链表中。

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param l1: ListNode l1 is the head of the linked list
     * @param l2: ListNode l2 is the head of the linked list
     * @return: ListNode head of linked list
     */
    ListNode * mergeTwoLists(ListNode * l1, ListNode * l2) {
        ListNode *merge = new ListNode(0);//定义新单链表的头结点
        ListNode *temp = merge;//定义一个指向头结点的头指针
        while(l1!=nullptr&&l2!=nullptr) {
            if(l1->val<l2->val) {
                temp->next = l1;//l1<l2把l1插入
                l1=l1->next;//向后迭代
            }else {
                temp->next = l2;//l1>l2把l2插入
                l2=l2->next;//向后迭代
            }

            temp = temp->next;//移动到新插入的节点上
        }
        if(l1==nullptr) {//l1为空,把l2的剩余部分全部插入进去
            temp->next = l2;
        }else{//l2为空,把l1的剩余部分全部插入进去
            temp->next = l1;
        }
        
        return merge->next;//返回头结点的后继结点
    }
};

9. 相反的顺序存储

题目详情

描述:
给出一个链表,并将链表的值以倒序存储到数组中。
注意:

  • 不能改变原始链表的结构
  • ListNode 有两个成员变量:ListNode.val 和 ListNode.next

样例

样例1

输入: 1 -> 2 -> 3 -> null
输出: [3,2,1]

样例2

输入: 4 -> 2 -> 1 -> null
输出: [1,2,4]

方法:
利用一个数组即可实现。先循环遍历列表,确定链表长度。然后建立一个与之大小相等的数组用于存放结点的数值。得到数组后,再把数组赋值给vector即可得到结果。

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head: the given linked list
     * @return: the array that store the values in reverse order 
     */
    vector<int> reverseStore(ListNode * head) {
        if(head == nullptr) {//判空,并对空情况进行处理
            vector<int> a;
            return a;
        }
        
        ListNode *p = head;//下面的一小段进行遍历,确定链表长度
        int num = 0;
        while(p!=nullptr) {
            ++num;
            p=p->next;
        }
        
        int s[num];//确定链表长度后,建立与之大小相等的数组,再把链表中的数值逆序存入
        p = head;
        for(int i = num - 1; i >= 0; --i) {
            s[i] = p->val;
            p=p->next;
        }
        
        vector<int> m;//把得到的数组赋值给vector
        for (int k = 0; k < num; ++k) {
            m.push_back(s[k]);
        }

        return m;//返回vector即可得到结果
    }
};

10. 删除链表中倒数第n个节点

题目详情

描述:
给定一个链表,删除链表中倒数第n个节点,返回链表的头节点。
注意:

  • 链表中的节点个数大于等于n

样例:

样例 1:
输出: list = 1->2->3->4->5->null, n = 2
输出: 1->2->3->5->null

样例 2:
输入: list = 5->4->3->2->1->null, n = 2
输出: 5->4->3->1->null

方法
给原来单链表添加一个头结点,然后遍历单链表得到单链表长度L。然后从头遍历L - n - 1个长度,去掉这一结点的后继结点即可达成目的。
原因:假设有5个结点,要删除倒数第二个结点,则向后挪动两次指针即可找到要被删除的结点的前驱结点。

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head: The first node of linked list.
     * @param n: An integer
     * @return: The head of linked list.
     */
    ListNode * removeNthFromEnd(ListNode * head, int n) {
        if(head == nullptr){
            return head;
        }
        
        ListNode *q = new ListNode(0);
        q->next = head;
        head = q;
        int counts = 0;
        while(q!=nullptr) {
            q=q->next;
            ++counts;
        }
        
        q = head;
        for(int i = 0; i < counts - n - 1; i++) {
            q = q->next;
        }
        
        q->next = q->next->next;
        
        return head->next;
    }
};

11. 两两交换链表中的节点

题目详情

描述:
给一个链表,两两交换其中的节点,然后返回交换后的链表。
注意:

  • 你的算法只能使用常数的额外空间,并且不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

样例:

样例 1:

输入:1->2->3->4->null
输出:2->1->4->3->null

样例 2:

输入:5->null
输出:5->null

方法:
给原先的链表添加一个头结点,然后进行常规的链表交换操作就可达到目的。但要注意交换时的逻辑,不要写错。

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head: a ListNode
     * @return: a ListNode
     */
    ListNode * swapPairs(ListNode * head) {
        if(head==nullptr || head->next == nullptr) {//判空
            return head;
        }  
        ListNode *phead = new ListNode(0);//添加头结点
        phead->next = head;
        ListNode *p = phead;//添加头指针
        
        while(p->next != nullptr && p->next->next != nullptr) {
            ListNode *temp = p->next->next;//把靠后的结点摘下来
            p->next->next = temp->next;//让靠后结点的后继结点不至于丢失
            temp->next = p->next;//常规交换
            p->next = temp;
            p=p->next->next;
        }
        
        return phead->next;
    }
};

12. 链表的中点

题目详情

描述:
找链表的中点。

样例:

样例 1:

输入: 1->2->3
输出: 2
样例解释: 返回中间节点的值

样例 2:

输入: 1->2
输出: 1
样例解释: 如果长度是偶数,则返回中间偏左的节点的值。

方法:
使用快慢指针的方法来寻找终点。
可以确保时间复杂度是 O(n/2)

快指针每次两步,慢指针每次一步。
快指针到尾的时候,慢指针就是中点。

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head: the head of linked list.
     * @return: a middle node of the linked list
     */
    ListNode * middleNode(ListNode * head) {
        if(head == NULL)
            return NULL;
        ListNode*p=head;
        int num = 0;
        while(p!=NULL) {
            p=p->next;
            num++;
        }
        
        num = (num +1)/2;
        while(head!=NULL) {
            if(num == 1) {
                return head;
            }
            --num;
            head=head->next;
        }
        
    }
};

13. 链表倒数第n个节点

题目详情
描述:
找到单链表倒数第n个节点,保证链表中节点的最少数量为n。

样例:

样例 1:

输入: list = 3->2->1->5->null, n = 2
输出: 1

样例 2:

输入: list  = 1->2->3->null, n = 3
输出: 1

方法:
先遍历链表得到表长L,然后从头开始,往后移动L-n个位置。再用一个新的链表结点保存这一个节点的值,并返回新建的结点。

AC代码:

/**
 * Definition of ListNode
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *         this->val = val;
 *         this->next = NULL;
 *     }
 * }
 */


class Solution {
public:
    /*
     * @param head: The first node of linked list.
     * @param n: An integer
     * @return: Nth to last node of a singly linked list. 
     */
    ListNode * nthToLast(ListNode * head, int n) {
        if(head == nullptr) {
            return head;
        }
        
        ListNode *p = head;
        int counts = 0;
        while(p!=nullptr) {
            ++counts;
            p=p->next;
        }
        
        p=head;
        for(int i =0; i < counts - n; i++) {
            p=p->next;
        }
        
        ListNode *s = new ListNode(p->val);
        
        return s;
    }
};

14. 两数相乘

题目详情
描述:
给出两个链表形式表示的数字,写一个函数得到这两个链表相乘乘积。

样例:

样例 1:

输入:9->4->6->null,8->4->null
输出:79464
解释:946*84=79464

样例 2:

输入:3->2->1->null,1->2->null
输出:3852
解释:321*12=3852

方法:
依次遍历两个链表。在遍历的时候用一个变量a来保存这链表表示的数值。每次循环时,a*=10,a+=val。这样就完成了数值还原。

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param l1: the first list
     * @param l2: the second list
     * @return: the product list of l1 and l2
     */
    long long multiplyLists(ListNode * l1, ListNode * l2) {
        if(l1 == nullptr || l2 == nullptr) {
            return 0;
        }
        
        ListNode *pointerA = l1;
        long numberA = 0;
        
        while(pointerA != nullptr) {
            numberA *= 10;
            numberA += pointerA->val;
            pointerA = pointerA->next;
        }
        
        ListNode *pointerB = l2;
        long numberB = 0;

        while(pointerB !=nullptr) {
            numberB *= 10;
            numberB += pointerB->val;
            pointerB = pointerB->next;
        }
        
        return numberA * numberB;
    }
};

15. 链表的中间结点

题目详情

描述:
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。

注意:

  • 给定的结点数值在1-100之间

样例:

样例 1:

输入:1->2->3->4->5->null
输出:3->4->5->null

样例 2:

输入:1->2->3->4->5->6->null
输出:4->5->6->null

方法:
使用快慢指针

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head: the head node
     * @return: the middle node
     */
    ListNode * middleNode(ListNode * head) {
        if(head == nullptr) {
            return head;
        }
        
       ListNode *fast = head;
       ListNode *low = head;
       
       while(fast!=nullptr && fast->next !=nullptr) {
           fast = fast->next->next;
           low = low->next;
       }
       
       return low;
    }
};

16. 链表的反向带权和

题目详情
描述:
给定一个链表,求出这个链表的带权和。链表结点的权值指的是该结点到链表尾节点的结点数。

样例:
样例1

输入: 3 -> 2 -> 5 -> 1
输出: 29
解释:
(3 * 4 + 2 * 3 + 5 * 2 + 1) = 29

样例2

输入: 1 -> 2 -> 3 -> 4
输出: 20
解释:
(1 * 4 + 2 * 3 + 3 * 2 + 4) = 20

方法:
先遍历一次链表得到链表长度,然后把链表对应位置的值与权值相乘即可得到结果。

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head: the given linked list
     * @return:  the weighted sum in reverse order
     */
    int weightedSumReverse(ListNode * head) {
        if(head == nullptr)
            return 0;
            
        ListNode *p = head;
        int counts = 0;
        
        while(p!=nullptr) {
            counts++;
            p=p->next;
        }
        
        p=head;
        int num = 0;
        while(p!=nullptr) {
            num += p->val * counts;
            --counts;
            p=p->next;
        }
        
        return num;
    }
};

17. 在O(1)时间复杂度删除链表节点

题目详情
描述:
给定一个单链表中的一个等待被删除的节点(非表头或表尾)。请在在 O(1) 时间复杂度删除该链表节点。

样例:

样例 1:

输入:
1->2->3->4->null
3
输出:
1->2->4->null
样例 2:

输入:
1->3->5->null
3
输出:
1->5->null

方法:
传统意义上,删除当前节点是从头结点head遍历,匹配到目标节点后删除,但是时间复杂度是O(n)。如果是删除下一个节点,时间复杂度则是O(1),因此考虑将下一个节点立面的data数据拷贝到当前节点(当前节点是带删除节点,其中数据可以覆盖),然后删除下一个节点。

AC代码:

/**
 * Definition of ListNode
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *         this->val = val;
 *         this->next = NULL;
 *     }
 * }
 */


class Solution {
public:
    /*
     * @param node: the node in the list should be deleted
     * @return: nothing
     */
    void deleteNode(ListNode * node) {
        if(node==NULL) {
            return;
        }
        ListNode *p = node->next;
        node->val = p->val;
        node->next = p->next;
        delete p;
    }
};

18. 队列维护

题目详情
描述:
实现一个队列的操作

  1. enqueue(item).将新元素放入队列中。
  2. dequeue(). 将第一个元素移出队列,返回它。

样例:

例1:

输入:
enqueue(1)
enqueue(2)
enqueue(3)
dequeue() // return 1
enqueue(4)
dequeue() // return 2

例2:

输入:
enqueue(10)
dequeue()// return 10

方法:
一个简单的入队出队操作。需要注意,本题没有给出结点类的定义,需要自己补充。注意结点类的实现方式,不同于结构体。而且在队列操作类中也没有给出队头队尾指针,都需要自己定义。first = last = nullptr。
判断队列是否为空可以使用 !first 。原因:nullptr与NULL在本质上都表示0,取非就是1,所以可以作为判断条件。

AC代码:

class Node {
public:
    int val;
    Node *next;

    Node(int val_) {
        this->val = val_;
        this->next = nullptr;
    }
};

class MyQueue {
public:
    /*
     * @param item: An integer
     * @return: nothing
     */
    
    Node *first, *last;

    MyQueue() {
        first = last = nullptr;
    }

    void enqueue(int item) {
        if (!first) {
            last = new Node(item);
            first = last;
        } else{
            last->next = new Node(item);
            last = last->next;
        }
    }

    /*
     * @return: An integer
     */
    int dequeue() {
        int m = first->val;
        auto k = first;
        first = first->next;
        delete k;
        return m;
    }
};

19. 链表划分

题目详情

描述:

给定一个单链表和数值x,划分链表使得所有小于x的节点排在大于等于x的节点之前。

你应该保留两部分内链表节点原有的相对顺序。

样例:

样例 1:

输入: list = null, x = 0
输出: null
样例解释: 空链表本身满足要求

样例 2:

输入: list = 1->4->3->2->5->2->null, x = 3
输出: 1->2->2->4->3->5->null
样例解释: 要保持原有的相对顺序。

方法:
采用双指针left和right。小于给定值的放到left指针后,大于等于的放到right指针后。

AC代码:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head: The first node of linked list
     * @param x: An integer
     * @return: A ListNode
     */
    ListNode *partition(ListNode *head, int x) {
        ListNode *leftDummy = new ListNode(0);
        ListNode *rightDummy = new ListNode(0);
        ListNode *leftTail = leftDummy;
        ListNode *rightTail = rightDummy;
        
        while (head != NULL) {
            if (head->val < x) {
                leftTail->next = head;
                leftTail = head;
            } else {
                rightTail->next = head;
                rightTail = head;
            }
            head = head->next;
        }
        
        leftTail->next = rightDummy->next;
        rightTail->next = NULL;
        
        return leftDummy->next;
    }
};

数组部分

简单难度

1.合并排序数组 II

题目详情
描述:

合并两个有序升序的整数数组A和B变成一个新的数组。新数组也要有序。

样例:

样例 1:

输入: A=[1], B=[1]
输出:[1,1]
样例解释: 返回合并后的数组。

样例 2:

输入: A=[1,2,3,4], B=[2,4,5,6]
输出: [1,2,2,3,4,4,5,6]
样例解释: 返回合并后的数组。

方法:

使用两个变量分别作为A,B数组的下标变量,然后逐个比较大小。小的那个放入新的数组。

AC代码:

class Solution {
public:
    /**
     * @param A: sorted integer array A
     * @param B: sorted integer array B
     * @return: A new sorted integer array
     */
    vector<int> mergeSortedArray(vector<int> &A, vector<int> &B) {
        vector<int> C;
        int i=0;
        int j=0;
        
        while(i < A.size() && j < B.size()) {
            if(A[i] > B[j]) {
                C.push_back(B[j]);
                j++;
            }else if(B[j] > A[i]){
                C.push_back(A[i]);
                i++;
            }else{
                C.push_back(A[i]);
                C.push_back(B[j]);
                i++;
                j++;
            }
   
        }
        
        while(i<A.size()) {
            C.push_back(A[i]);
            ++i;
        }
        while(j < B.size()) {
            C.push_back(B[j]);
            ++j;
        }
        
        return C;
    }
    
};

2.数组划分

题目详情

描述:

给出一个整数数组 nums 和一个整数 k。划分数组(即移动数组 nums 中的元素),使得:

所有小于k的元素移到左边,所有大于等于k的元素移到右边

返回数组划分的位置,即数组中第一个位置 i,满足 nums[i] 大于等于 k。

注意:你应该真正的划分数组 nums,而不仅仅只是计算比 k 小的整数,如果数组 nums 中的所有元素都比 k 小,则返回 nums.length。

样例:

例1:

输入:
[],9

输出:
0

例2:

输入:
[3,2,2,1],2

输出: 1

解释:
真实的数组为[1,2,2,3].所以返回 1

方法:
使用双指针。指针i指向数组开头,指针j指向数组结尾。
① 当两指针没有相遇时
若左边指针指向的数组元素小于划分标杆K时,左指针向右移动。
若右边指针指向的数组元素小于划分标杆K时,左指针向左移动。
② 当两指针没有相遇并且左指针指向的数组元素大于K,右指针指向的数组元素小于K,那么交换两指针所指元素。并按①挪动指针。
③ 返回左指针对应的数组下标。

AC代码:

class Solution {
public:
    /**
     * @param nums: The integer array you should partition
     * @param k: An integer
     * @return: The index after partition
     */
    int partitionArray(vector<int> &nums, int k) {
        if(nums.size() == 0)
            return 0;
        
        int i = 0;
        int j = nums.size() - 1;
        
        while(i <= j) {
            while(i <=j && nums[i] < k) i++;
            while(i <= j && nums[j]>=k) --j;
            if(i<=j) {
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
                i++;
                j--;
            }
        }
        
        return i;
    }
};

3.主元素Ⅰ

题目详情

描述:
给定一个整型数组,找出主元素,它在数组中的出现次数严格大于数组元素个数的二分之一。

样例:

样例 1:

输入: [1, 1, 1, 1, 2, 2, 2]
输出: 1

样例 2:

输入: [1, 1, 1, 2, 2, 2, 2]
输出: 2

要求时间复杂度为O(n),空间复杂度为O(1)

方法:
定义一个变量counts并初始化为 0 ,用来记录当前元素出现次数;currentNumber记录正在被比较的元素。只要被比较元素与当前数组元素不相等,那么就把counts减 1 ,当counts为 0 时,把currentNumber替换成现在的数组元素。

AC代码:

class Solution {
public:
    /*
     * @param nums: a list of integers
     * @return: find a  majority number
     */
    int majorityNumber(vector<int> &nums) {
        int counts = 0;//记录元素出现次数
        int currentNumber;//记录正在被比较的元素。
        
        for (int i = 0; i < nums.size(); ++i) {
            if(counts==0) {
                currentNumber = nums[i];
            }
            
            if(currentNumber!=nums[i]) {
                --counts;
            }else{
                ++counts;
            }
        }
        
        return currentNumber;
    }
};

4.翻转数组

题目详情

描述:

原地翻转给出的数组 nums

样例:

样例 1:

输入 : nums = [1,2,5]
输出 : [5,2,1]

方法:

以数组中间为界,左右对换元素

AC代码:

class Solution {
public:
    /**
     * @param nums: a integer array
     * @return: nothing
     */
    void reverseArray(vector<int> &nums) {
        if(nums.size()==0)
            return;
            
        int j = nums.size()-1;
        for (int i = 0; i < nums.size() / 2 && i!=j; ++i) {
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
            j--;
        }
    }
};

技巧型

简单难度

1. 尾部的零

题目详情

描述:

设计一个算法,计算出n阶乘中尾部零的个数。

样例:

样例 1:
输入: 11
输出: 2

样例解释: 
11! = 39916800, 结尾的0有2个。

样例 2:
输入: 5
输出: 1

样例解释: 
5! = 120, 结尾的0有1个

方法:

看输入的输由多少个5相加组成,有多少个5,就有多少0。
切忌直接算阶乘,然后结尾计算有多少个0,遇到大数的阶乘,long long也存不下,程序会爆掉。

举例:

输入:11
计算:11 / 5 = 2;

输入:105
计算:105 / 5 = 25;

AC代码:

class Solution {
public:
    /*
     * @param n: A long integer
     * @return: An integer, denote the number of trailing zeros in n!
     */
    long long trailingZeros(long long n) {
        long number = n / 5;
        long count = 0;
        while (number > 0){
            count += number;
            number /= 5;
        }
        return count;
    }
};
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

随风舞落叶殇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值