算法刷题日记day1

206 阅读4分钟

链表

1.链表反转

ListNode* ReverseList(ListNode* pHead) {
   ListNode *pre=nullptr;
    ListNode*cur=pHead;
    ListNode*nex=nullptr;
    while(cur)
    {
        nex=cur->next;
        cur->next=pre;
        pre=cur;
        cur=nex;
    }
   return pre;
}

2.链表内指定区间反转

ListNode* reverseBetween(ListNode* head, int m, int n) {
      if(head==NULL||head->next==NULL||m==n) return head;
      struct ListNode* p;
      struct ListNode* q;
      struct ListNode* l;
      l = (struct ListNode*)malloc(sizeof(struct ListNode));  //创建空白节点
      l->next = head;  // 空白节点指向头节点
      p = q = l;
      for(int i =1;i<m;i++)
          p = p->next;  // p指向m前一个节点
      for(int i = 1;i<=n;i++)
          q = q->next; //q 指向第n个节点
      struct ListNode* p1 = p->next;  // p1 是第m个节点
      struct ListNode* p2 = p1->next;
      p->next = q; // m前一个节点要指向q
      p1->next = q->next; 
      while(p2!=q){  //反转节点
          p = p2->next;
          p2->next = p1;
          p1 = p2;
          p2 = p;
      }
      p2->next = p1;
      return l->next;
}

FABD0D3A2998BA407BEC776CE44CAFD3.jpg

3.链表内每k个节点为一组反转

ListNode* reverseKGroup(ListNode* head, int k) {
    if(k<=1) return head;
    if(!head) return head;
    ListNode *left=head;
    ListNode*right=head;
    for(int i=0;i<k;i++)//让右指针指向第k个节点
    {
        if(!right) return head;
        right=right->next;
    }
    ListNode* node=reverseLink(left,right);//每k个节点为一组反转
    left->next=reverseKGroup(right, k);//反转之后的右指针为left,指向下一组k个节点的right
    return node;
}
ListNode* reverseLink(ListNode* left,ListNode* right)
{
    ListNode* pre = nullptr;
    ListNode *next = nullptr;
    while (left != right)
    {
        next = left->next;
        left->next = pre;
        pre = left;
        left = next;
    }
return pre;
}

4.合并两个排序的链表

ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
    ListNode *vhead = new ListNode(-1);//创建空白节点
    ListNode *cur = vhead;
    while (pHead1 && pHead2) {
        if (pHead1->val <= pHead2->val) {
            cur->next = pHead1;
            pHead1 = pHead1->next;
        }
        else {
            cur->next = pHead2;
            pHead2 = pHead2->next;
        }
        cur = cur->next;
    }
    cur->next = pHead1 ? pHead1 : pHead2;//把剩余的节点接到已合并链表之后
    return vhead->next;
}

5.合并k个已排序的链表,返回一个升序的链表

时间复杂度O(nlogn)

ListNode *mergeKLists(vector<ListNode *> &lists) {//传入参量是一个存放k个链表的容器
    priority_queue<int,vector<int>,greater<int>> q;//优先队列默认大顶堆,此时需改成小顶堆
    for(int i=0;i<lists.size();i++)//把k个链表的所有节点入队
    {
        ListNode *t=lists[i];
        while(t)
        {
            q.push(t->val);
            t=t->next;
        }
    }
    ListNode *head=new ListNode(0);//创建空白头结点
    ListNode* p=head;
    while(!q.empty())//构造新链表
    {
        p->next=new ListNode(q.top());
        q.pop();
        p=p->next;
    }
    return head->next;
}

6.判断链表中是否有环

思路:快慢指针法,快指针每次走两步,慢指针每次走一步,如果相遇则有环

bool hasCycle(ListNode *head) {
    if(head==nullptr) return false;//判断边界条件
    ListNode * fast = head;
    ListNode * slow = head;
    while(fast&&fast->next)//循环条件
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
            return true;
    }
    return false;
}

7.链表中环的入口节点

思路:快慢指针法,快指针速度是慢指针的两倍,快慢指针第一次相遇时快指针所走路程是慢指针的两倍,此时让快指针回到头结点,速度和慢指针相同,再次相遇的节点就是环的入口节点。

ListNode* EntryNodeOfLoop(ListNode* pHead) {
    ListNode * fast = pHead;
    ListNode * slow = pHead;
    while(fast&&fast->next)//循环条件
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow) break;//第一次相遇时跳出循环
    }
    if(!fast||!fast->next) return nullptr;//如果链表无环,返回空指针
    fast=pHead;//快指针回到头结点
    while(fast!=slow)
    {
        fast=fast->next;
        slow=slow->next;
    }
    return fast;//第二次相遇时则为环的入口节点
}

链表中的倒数k个节点

思路:快慢指针法,快指针先走k步,快慢指针同时开始走,当快指针走到终点时,慢指针所指的就是链表的倒数第k个节点。

 ListNode* FindKthToTail(ListNode* pHead, int k) {
    ListNode * fast=pHead;
    ListNode * slow = pHead;
    for(int i=0;i<k;i++)//快指针先走k步
        {
            if(!fast) return nullptr;//边界条件:链表小于k,返回空指针
            fast=fast->next;
        }
    while(fast)
    {
        
        slow=slow->next;
        fast=fast->next;
    }
    return slow;
}

删除链表的倒数第n个节点

格外注意边界条件的判断

ListNode* removeNthFromEnd(ListNode* head, int n) {
    if(!head) return nullptr;//边界条件:链表为空,返回空指针
    ListNode * fast = head;
    ListNode * slow = head;
    ListNode * p=new ListNode(0);//创建空白头结点
    p->next=head;
    for(int i = 0;i<n;i++)//快指针先走n步
    {
        fast=fast->next;
    }
    if(!fast) //如果快指针不存在,说明要删除的是头结点
    {
        p->next=p->next->next;
        return p->next;
    }
    while(fast->next!=nullptr)//找到倒数第n+1个节点
    {
        slow=slow->next;
        fast=fast->next;
    }
    slow->next=slow->next->next;//删除
    return head;
}

10.两个链表的第一个公共节点

描述:输入两个无环的单向链表,找到第一个公共节点并返回,如果没有公共节点,返回空指针

ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
    ListNode *ta = pHead1, *tb = pHead2;//定义两个工作指针,遍历链表
    while (ta != tb) {
        ta = ta ? ta->next : pHead2;//ta指针走到终点,则从pHead2开始遍历
        tb = tb ? tb->next : pHead1;//tb指针走到终点,则从pHead1开始遍历
    }
    //若无公共部分,两指针都走了m+n(m+n为两表长之和)的长度,同时指向空,不满足循环条件,跳出循环
    return ta;