第2章 线性表综合习题(leetcode)

来源:互联网 发布:素数java 编辑:程序博客网 时间:2024/06/04 18:59

这里选择一些关于线性表的leetcode的习题。这些习题主要以顺序表和链表为主,需要熟练掌握顺序表和单链表的基本操作,同时综合使用之前学习过的一些算法设计方法和技巧。
习题的顺序与难度无关。

删除有序数组重复元素(Remove Duplicates from Sorted Array)

给定一个已经排序的数组,删除里面的重复元素,保证每个元素只出现一次,并返回新的数组长度。要求使用O(1)的空间复杂度。例如[1,1,2]应该得到[1,2],长度为2

解这个问题,应该很容易联系到2.2节的例题——删除所有指定值的元素。有两种思路,一个是维护新元素需要迁移的位数,一个是维护新元素的新的下标,即相当于重建线性表。这里选择第二种思路,更容易实现操作。

class Solution {public:    int removeDuplicates(vector<int>& nums) {        if(nums.empty()){            return 0;        }        int i;        int newid = 1;        for(i = 1; i < nums.size(); ++i){            if(nums[newid - 1] != nums[i]){                nums[newid++] = nums[i];            }        }        return newid;    }};

反转链表的一部分(Reverse Linked List II)

给定一个链表和两个数mn,反转第mn个结点。
例如1->2->3->4->5->NULLm=2,n=4
应该得到1->4->3->2->5->NULL
要求只循环链表一次,不能使用额外空间。

反转序列,可以考虑用头插法去解决;所以可以从头开始计数,找到第m个结点后,将第m1个结点看作新表的头结点,然后开始使用头插法,直到计数器为n。为了能够将n+1开始的结点正确的连接在后面,使用头插法一样需要保留一个尾指针ttail

class Solution {public:    ListNode* reverseBetween(ListNode* head, int m, int n) {        ListNode * nhead = new ListNode(0);        nhead->next = head;        int id = 0;        ListNode * p = nhead;        while(p != nullptr){            if(id == m - 1){                ListNode * thead = p;                ListNode * ttail = p->next;                p = p->next;                while(id != n){                    ListNode * q = p->next;                    p->next = thead->next;                    thead->next = p;                    p = q;                    id++;                }                ttail->next = p;                break;            }else{                p = p->next;                id++;            }        }        return nhead->next;     }};

旋转链表(Rotate List)

给定一个链表,将其向右旋转k位。k0。要求使用常数空间复杂度。
例如1->2->3->4->5->NULL,k = 2
应该得到4->5->1->2->3->NULL

实际上可以把链表看作两部分。1nk整体向后移动,而剩下的k个结点放到前面来。因此为了寻找第nk个结点,先需要求出链表长度n,然后寻找nk的结点,分别将它们看作两个表,修改他们的头尾指针指向就可以了。

class Solution {public:    ListNode* rotateRight(ListNode* head, int k) {        if(!head){            return head;        }        int count = 0;        ListNode * p = head;        while(p != nullptr){            count++;            p = p->next;        }        int actual_offset = k % count;        p = head;        for(int i = 0; i != actual_offset; i++){            p = p->next;        }        ListNode * q = head;        while(p->next != nullptr){            p = p->next;            q = q->next;        }        p->next = head;        head = q->next;        q->next = nullptr;        return head;    }};

判断单链表是否对称(Palindrome Linked List)

给定一个链表,判断其是否对称。要求使用O(n)的时间复杂度和O(1)的空间复杂度。

判断对称很容易想到,使用两个指针,分别从头尾相向扫描。但是单链表不具备这个操作。实际上可以先找到链表的中点,然后将中点以后的元素使用头差法将其逆序,这样就得到了一个新的链表(后半段的逆序),同时从头扫描两个表,就相当与用两个指针分别从头尾开始扫描。只不过这种做法将原有的链表破坏了。
需要注意的是,使用双指针法找链表中点后将其划分时,两个链表长度最多相差1。不影响判断对称性。

class Solution {public:    bool isPalindrome(ListNode* head) {        if(head == nullptr){            return true;        }        ListNode * list_head = new ListNode(0);        list_head->next = head;        ListNode * p1 = list_head;        ListNode * p2 = list_head;        while(p1 != nullptr){            if(p1->next != nullptr){                p1 = p1->next->next;            }else{                break;            }            p2 = p2->next;        }        ListNode * q = p2->next;        p2->next = nullptr;        ListNode * head_list2 = new ListNode(0);        ListNode * head_list1 = new ListNode(0);        head_list2->next = head;        while(q != nullptr){            ListNode * tmp = q->next;            q->next = head_list1->next;            head_list1->next = q;            q = tmp;        }        bool is_palindrome = true;        ListNode * p = head_list1->next;        q = head_list2->next;        while(p != nullptr && q != nullptr){            if(p->val != q->val){                is_palindrome = false;                break;            }            p = p->next;            q = q->next;        }        return is_palindrome;    }};

判断链表是否成环(Linked List Cycle )

不使用额外空间,判断一个链表是否有环。

这道题也是双指针法的使用,判断走得快的和走得慢的是否会相遇就行了。题目给的链表是不带头结点的,因此不能将pq都是head的情况进行比较。

class Solution {public:    bool hasCycle(ListNode *head) {        if(!head){            return false;        }        bool flag = false;        ListNode * p1 = head->next;        ListNode * p2 = head;        while(p1 != nullptr && p2 != nullptr){            if(p1 == p2){                flag = true;                break;            }            if(p1->next){                p1 = p1->next->next;            }else{                break;            }            p2 = p2->next;        }        return flag;    }};

链表的奇偶结点划分(Odd Even Linked List)

将一个给定的单链表按照结点序号的奇偶性进行划分,前面是奇数序号结点,后面是偶数序号结点。
这道题实际上也是一个重新建表的过程。分别使用尾插法建表就可以了。

class Solution {public:    ListNode* oddEvenList(ListNode* head) {        ListNode * head1 = new ListNode(0);        ListNode * head2 = new ListNode(0);        ListNode * tail1 = head1;        ListNode * tail2 = head2;        ListNode * p = head;        int count = 1;        while(p != nullptr){            if(count % 2){                tail1->next = p;                tail1 = p;            }else{                tail2->next = p;                tail2 = p;            }            p = p->next;            count++;        }        ListNode * ans_head = head1->next;        tail1->next = head2->next;        tail2->next = nullptr;        delete head1;        delete head2;        return ans_head;    }};
原创粉丝点击