链表操作_手撕

1、链表中倒数第k个节点

题目:输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。

ListNode* FindKthToTail(ListNode* pListHead, unsigned int k)
{
    ListNode *pAhead = pListHead;
    ListNode *pBehind = nullptr;
    
    for(unsigned int i = 0; i < k-1; i++)
    {
        pAhead = pAhead->m_pNext;
    }
    
    pBehind = pListHead;
    while(pAhead->m_pNext != nullptr)
    {
        pAhead = pAhead->m_pNext;
        pBehind = pBehind->m_pNext;
    }
    return pBehind;
}

2、链表中环的入口节点

题目:如果一个链表中包含环,如何找出环的入口节点?

ListNode* MeetingNode(ListNode* pHead)
{
    if(pHead == nullptr)
        return nullptr;
    
    ListNode* pSlow = pHead->m_pNext;
    if(pSlow == nullptr)
        return nullptr;
    
    ListNode* pFast = pSlow->m_pNext;
    while(pFast != nullptr && pSlow != nullptr)
    {
        if(pFast == pSlow)
            return pFast;
        
        pSlow = pSlow->m_pNext;
        pFast = pFast->m_pNext;
        if(pFast != nullptr)
            pFast = pFast->m_pNext;
    }
    return nullptr;
}

ListNode* EntryNodeOfLoop(ListNode* pHead)
{
    ListNode* meetingNode = MeetingNode(pHead);
    if(meetingNode == nullptr)
        return nullptr;
    
    int nodesInLoop = 1;
    ListNode* pNode1 = meetingNode;
    while(pNode1->m_pNext != meetingNode)
    {
        pNode1 = pNode1->m_pNext;
        ++nodesInLoop;
    }
    
    pNode1 = pHead;
    for(int i = 0; i < nodesInLoop; ++i)
        pNode1 = pNode1->m_pNext;
    
    ListNode* pNode2 = pHead;
    while(pNode1 != pNode2)
    {
        pNode1 = pNode1->m_pNext;
        pNode2 = pNode2->m_pNext;
    }
    return pNode1;
}

3、反转链表

题目:定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

ListNode* ReverseList(ListNode* pHead)
{
    ListNode* pReversedHead = nullptr;
    ListNode* pNode = pHead;
    ListNode* pPrev = nullptr;
    while(pNode != nullptr)
    {
        ListNode* pNext = pNode->m_pNext;
        if(pNext == nullptr)
            pReversedHead = pNode;
        
        pNode->m_pNext = pPrev;
        
        pPrev = pNode;
        pNode = pNext;
    }
    return pReversedHead;
}

4、合并两个排序的链表

题目:输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
    if(pHead1 == nullptr)
        return pHead2;
    else if(pHead2 == nullptr)
        return pHead1;
    
    ListNode* pMergedHead = nullptr;
    
    if(pHead1->m_nValue < pHead2->m_nValue)
    {
        pMergedHead = pHead1;
        pMergedHead->m_pNext = Merge(pHead1->m_pNext, pHead2);
    }
    else
    {
        pMergedHead = pHead2;
        pMergedHead->m_pNext = Merge(pHead1, pHead2->m_pNext);
    }
    return pMergedHead;
}
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
//加个链表头
    struct ListNode head;
    struct ListNode *pre = &head;

    while (l1 && l2) {
        if (l1->val <= l2->val) {
            pre->next = l1;
            l1 = l1->next;
        } else {
            pre->next = l2;
            l2 = l2->next;
        }
        pre = pre->next;
    }
    //将剩下元素直接接在最后面
    pre->next = (NULL == l1 ? l2 : l1);
    return head.next;
}

5、树的子结构

题目:输入两棵二叉树A和B,判断B是不是A的子结构。

bool HasSubtree(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
{
    bool result = false;
    
    if(pRoot1 != nullptr && pRoot2 != nullptr)
    {
        if(Equal(pRoot1->m_dbValue, pRoot2->m_dbValue))
            result = DoesTree1HaveTree2(pRoot1, pRoot2);
        if(!result)
            result = HasSubtree(pRoot1->m_pLeft, pRoot2);
        if(!result)
            result = HasSubtree(pRoot1->m_pRight, pRoot2);
    }
    return result;
}

bool DoesTree1HaveTree2(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
{
    if(pRoot2 == nullptr)
        return true;
    if(pRoot1 == nullptr)
        return false;
    
    if(!Equal(pRoot1->m_dbValue, pRoot2->m_dbValue))
        return false;
    
    return DoseTree1HaveTree2(pRoot1->m_pLeft, pRoot2->m_pLeft) && DoseTree1HaveTree2(pRoot1->m_pRight, pRoot2->m_pRight);
}

bool Equal(double num1, double num2)
{
    if((num1 - num2 > -0.0000001) && (num1 - num2 < 0.0000001))
        return true;
    else
        return false;
}

6、判断单链表中是否存在环

bool IsExitsLoop(List *head)
{
    List *slow = head, *fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast) break;
    }
    return !(fast == NULL || fast->next == NULL);
}

7、回文链表

编写一个函数,检查输入的链表是否是回文的

思路:利用双指针找到链表的中间位置,然后将后半段反转,将后半段依次与前半段比较。

bool isPalindrome(struct ListNode* head){
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    struct ListNode* p = head;
    while((fast!=NULL) &&(fast->next!=NULL))
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    struct ListNode *cur = slow;
    struct ListNode *pre = NULL;
     //将链表的后半段反转
    while(cur!=NULL){
        //保存下一个的值
        struct ListNode* temp = cur->next;
        //重新连接当前节点
        cur->next = pre;
        //更新pre
        pre = cur;
        //cur指向下一个
        cur = temp;
    }
   // 将后半段反转的链表与前半段依次比较
    while(pre&&head){
        if(pre->val!=head->val)
            return false;
        pre=pre->next;head=head->next;
    }
    return true;

}

8、移除重复节点

编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。

思路1:定义两个指针current和p来逐个遍历链表,current元素依次和p比较,直到p为NULL,current向后移动一个。

思路2:利用标记数组,标记当前值是否出现过。

方法1:
struct ListNode* removeDuplicateNodes(struct ListNode* head){
  	//判断是否需要遍历
    if (head == NULL) return NULL;
    struct ListNode* current = head;
	//双指针逐个比较
    while (current) {
        struct ListNode* p = current;
        while(p->next) {
            if (p->next->val == current->val) {
                p->next = p->next->next;
            } else {
                p = p->next;
            }
        }
        current = current->next;
    }
    return head;
}
方法2:
struct ListNode* removeDuplicateNodes(struct ListNode* head){
    //判断是否需要遍历
    if(head==NULL || head->next==NULL)  
        return head;
     //利用val的值在0~20000之间,标记是否出现过
    int index[20001] = {0}; 
    index[head->val] = 1;
    struct ListNode *pre = head, *q = head->next;
    while(q)
    {
     //当前val未出现过,保存当前val,修改pre和p指针
        if(index[q->val]==0)   
        {
            index[q->val] = 1;
            pre = q;
            q = q->next;
        }
        //当前值已经出现过了,删除当前节点
        else    
        {
            pre->next = q->next;
            q = pre->next;
        }
    }
    return head;
}

9、原地移除元素

一个(慢)num指针为数组本身进行复写,一个(快) p指针不断移动并判断是否等于val的值。一旦快指针所指向的值不等于val,则进行复写,将快指针所指向的值复写慢指针所指向的值,且计数器加一。(注意顺序:先*nums=p[i], 后nums++; 因为慢指针的最后一项总是待复写的位置,所以若快指针所指向的值不等于val时,先将快指针的值复写慢指针的值,将慢指针往后挪一位)

int removeElement(int* nums, int numsSize, int val){

    int count=0;
    int *p=nums;
    for(int i=0;i<numsSize;i++){
        if(p[i]!=val){
            *nums=p[i];
            nums++;
            count++;
        }
    }
    return count;
}

10、寻找数组的中心索引

中心索引:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。

方法一:
int pivotIndex(int* nums, int numsSize){

    if (numsSize < 3) 
    {
        return -1;
    }
    int sum = 0,right = 0,left = 0;
    for(int i = 0;i <numsSize;i++)
    {
        sum+=nums[i];
    }
    for(int i = 0;i < numsSize;i++)
    {
        //计算右边的和
        right = sum-nums[i]-left; 
        //左右相等则返回
        if(left == right)
            return i;
        //左边求和
        left += nums[i];      
    }
    return -1;
}
方法二:
/*
*  1. 从位置0开始右移寻找,先计算left和right,然后每右移一位判断是否符合
*/
int pivotIndex(int* nums, int numsSize)
{
    if (numsSize < 3) {
        return -1;
    }
    int left = 0;
    int right = 0;
    for (int i = 1; i < numsSize; ++i) {
        right += nums[i];
    }

    int pos = 0;
    while (left != right) {
        if (pos + 1 == numsSize) {
            return -1;
        }
        left += nums[pos];
        right -= nums[pos + 1];
        pos++;
    }
    return pos;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值