4,链表【p5】

链表

  • 4.1哈希表简介
  • 4.2有序表简介
  • 4.3链表
    • 4.3.1例1-反转单向和双向链表
    • 4.3.2例2-打印两个有序链表的公共部分
    • 4.3.3面试时链表解题的方法论
    • 4.3.4例3-判断一个链表是否为回文结构
      • 4.3.4.1快慢指针
    • 4.3.5例4-将单向链表按某值划分成左边小、中间相等、右边大的形式
    • 4.3.6例5-复制好友随机指针节点的链表
      • 4.3.6.1方法1-利用哈希表
      • 4.3.6.1方法2-不利用哈希表
    • 4.3.7例6-两个单链表相交的一系列问题
      • 4.3.7.1判断环方法1哈希表
      • 4.3.7.2判断环方法2快慢指针
        • 4.3.7.3情况1都无环
        • 4.3.7.4情况2两个链表其中一个有环,不可能相交
        • 4.3.7.5情况3两个链表都有环

4.1哈希表简介

使用哈希表,时间复杂度被认为是常数级别O(N)

哈希表在使用层面上可以理解为一种集合结构
如果只有key,没有伴随数据value,可以使用HashSet结构(C++中叫UnOrderedSet / UnSortedSet)
如果既有key,又有伴随数据value,可以使用HashMap结构(C++中叫UnOrderedMap / UnSortedMap)
有无伴随数据,是HashMap和HashSet唯一的区别,底层的实际结构的一回事
使用哈希表增(put)、删(remove)、改(put)(更新的不是key是value)、查(get)的操作,可以认为时间复杂度为O(1),但是常数时间比较大
放入哈希表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小
放入哈希表的东西,如果不是基础类型,内部按引用传递,内存占用是这个东西内存地址的大小

(key如果是一个长字符串,哈希表在基础类型时,值传递,所以拷贝此长字符串,所以造成空间使用很多。哈希表在不是基础类型时,引用传递,所以拷贝此长字符串地址放在哈希表内)
(nodeA=new Node(1); nodeB=new Node(1);这两个在哈希表中不是一个东西,因为nodeA有自己的地址, nodeB也有自己的地址,虽然 nodeA和 nodeB值相同,但是哈希表根据地址做的划分)
请添加图片描述
请添加图片描述

4.2有序表简介

性能相对哈希表差一点,时间复杂度O(logN)

有序表在使用层面上可以理解为一种集合结构
如果只有key,没有伴随数据value,可以使用TreeSet结构(C++中叫OrderedSet)
如果既有key,又有伴随数据value,可以使用TreeSet结构(C++中叫OrderedMap)
有无伴随数据,是TressSer和TressMap唯一的区别,底层的实际结构的一回事
有序表和哈希表的区别是,有序表把key按照顺序组织起来,而哈希表完全不组织
红黑树、AVL树、size-balance-tree和跳表等都属于有序表结构,只是底层具体实现不同
放入有序表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小
放入有序表的东西,如果不是基础类型,必须提供比较器(告诉编译器怎么排序),内部按引用传递,内存占用是这个东西内存地址的大小
不管是什么底层具体实现,只要是有序表,都有以下固定的基本功能和固定的时间复杂度

请添加图片描述
有序表的固定操作:

  1. void put(K key,V value): 将一个 (key,value) 记录加入到表中,或者将key的记录更新成value。
    2)V get(K key):根据给定的key,查询value并返回
  2. void remove(K key):移除key的记录。
    4)boolean containsKey(K key):问是否有关于key的记录。
    5)K firstKey():返回所有键值的排序结果中,最左(最小)的那个。
    6)K lastKey():返回所有键值的排序结果中,最右(最大)的那个。
    7)K floorKey(K key):如果表中存入过key,返回key; 否则返回所有键值的排序结果中key的前一个。
    8)K ceilingKey(K key):如果表中存入过key,返回key;否则返回所有键值的排序结果中key的后一个。

4.3链表

单链表的节点结构

Class Node<v>
{
    V value;
    Node next;
}

由以上结构的节点依次连接起来所形成的链叫单链表结构

双链表的节点结构

Class Node<v>
{
    V value;
    Node next;
    Node last;
}

由以上结构的节点依次连接起来所形成的链叫双链表结构

单链表和双链表结构只需要给定一个头部节点head,就可以找到剩下的所有的节点

创建一个单向链表的过程

#include <iostream>
#include <string>

using namespace std;

struct Student {
    string name;
    int age;
    string major;
    Student* next;
};

class StudentList {
private:
    Student* head;

public:
    StudentList() {
        head = nullptr;
    }

    void addStudent(const string& name, int age, const string& major) {
        // 创建新的学生节点
        Student* newStudent = new Student;
        newStudent->name = name;
        newStudent->age = age;
        newStudent->major = major;
        newStudent->next = nullptr;//表示链表没有下一个节点

        // 如果链表为空,将新节点作为头节点
        if (head == nullptr) {
            head = newStudent;
        }
        else {
            // 找到链表最后一个节点,将新节点插入到链表末尾
            Student* current = head;
            while (current->next) {
                current = current->next;
            }
            current->next = newStudent;
        }
    }

    void displayStudents() {
        Student* current = head;
        if (current == nullptr) {
            cout << "学生列表为空." << endl;
            return;
        }
        while (current) {
            cout << "姓名: " << current->name << ", 年龄: " << current->age << ", 专业: " << current->major << endl;
            current = current->next;
        }
    }
};

int main() {
    // 创建学生链表对象
    StudentList studentList;

    // 添加学生信息
    studentList.addStudent("小明", 18, "计算机科学");
    studentList.addStudent("小红", 19, "数学");
    studentList.addStudent("小李", 20, "物理");

    // 显示学生信息
    studentList.displayStudents();

    return 0;
}

创建一个双向链表的过程

#include <iostream>
#include <string>

using namespace std;

struct Student {
    string name;
    int age;
    string major;
    Student* prev; // 添加前驱指针
    Student* next; // 添加后继指针
};

class StudentList {
private:
    Student* head;

public:
    StudentList() {
        head = nullptr;
    }

    void addStudent(const string& name, int age, const string& major) {
        // 创建新的学生节点
        Student* newStudent = new Student;
        newStudent->name = name;
        newStudent->age = age;
        newStudent->major = major;
        newStudent->prev = nullptr;
        newStudent->next = nullptr;

        // 如果链表为空,将新节点作为头节点
        if (head == nullptr) {
            head = newStudent;
        }
        else {
            // 找到链表最后一个节点,将新节点插入到链表末尾
            Student* current = head;
            while (current->next) {
                current = current->next;
            }
            current->next = newStudent;
            newStudent->prev = current;
        }
    }

    void displayStudents() {
        Student* current = head;
        if (current == nullptr) {
            cout << "学生列表为空." << endl;
            return;
        }
        while (current) {
            cout << "姓名: " << current->name << ", 年龄: " << current->age << ", 专业: " << current->major << endl;
            current = current->next;
        }
    }

};

int main() {
    // 创建学生链表对象
    StudentList studentList;

    // 添加学生信息
    studentList.addStudent("小明", 18, "计算机科学");
    studentList.addStudent("小红", 19, "数学");
    studentList.addStudent("小李", 20, "物理");

    // 显示学生信息
    studentList.displayStudents();

    return 0;
}

4.3.1例1-反转单向和双向链表

【题目】分别实现反转单向链表和反转双向链表的函数
【要求】如果链表长度为N,时间复杂度要求为O(N),额外空间复杂度要求为O(1)

本身链表为1节点连2节点,2节点连3节点
反转操作为3节点连2节点,2节点连1节点

如果没有换头操作,可以定义为void类型。如果换头了,那就需要带有返回值(本身链表为1节点连2节点,2节点连3节点,123。反转操作为3节点连2节点,2节点连1节点,321.所以head=f(head),head指向3节点了)

在上述单向链表代码中StudentList类中加入,调用时即可反转单向链表

void ReverseList() {
        if (head == nullptr || head->next == nullptr) {
            return;
        }

        Student* prev = nullptr;
        Student* current = head;
        Student* next = nullptr;

        while (current != nullptr) {
            // 保存下一个节点的指针
            next = current->next;

            // 反转当前节点的指针
            current->next = prev;

            // 移动指针位置
            prev = current;
            current = next;
        }

        // 更新头节点指针
        head = prev;
    }

在上述双向链表代码中StudentList类中加入,调用时即可反转单向链表

void reverseList() {
        if (head == nullptr || head->next == nullptr) {
            // 空链表或只有一个节点,无需反转
            return;
        }

        Student* current = head;
        Student* prev = nullptr;

        while (current) {
            // 保存当前节点的下一个节点
            Student* next = current->next;

            // 反转当前节点的指针
            current->next = prev;
            current->prev = next;

            // 更新prev和current指针
            prev = current;
            current = next;
        }

        // 更新头节点
        head = prev;
    }

4.3.2例2-打印两个有序链表的公共部分

【题目】给定两个有序链表的头指针head1和head2,打印两个链表的公共部分
【要求】如果两个链表的长度之和为N,时间复杂度要求为O(N),额外空间复杂度要求为O(1)
请添加图片描述
链表1指针先指向1,链表2指针先指向0,
对比,谁小谁移动,链表2指针先指向2,
对比,谁小谁移动,链表1指针先指向2,
对比,相等,打印2,共同移动,链表1指针先指向5,链表2指针先指向3,
对比,谁小谁移动,链表2指针先指向5,
对比,相等,打印5,共同移动,越界,停止
打印出看2,5

此方法注意要升序排列

#include <iostream>
#include <string>

using namespace std;

struct Student {
    string name;
    int age;
    string major;
    Student* next;
};

class StudentList {
private:
    Student* head;

public:
    StudentList() {
        head = nullptr;
    }

    void addStudent(const string& name, int age, const string& major) {
        // 创建新的学生节点
        Student* newStudent = new Student;
        newStudent->name = name;
        newStudent->age = age;
        newStudent->major = major;
        newStudent->next = nullptr;//表示链表没有下一个节点

        // 如果链表为空,将新节点作为头节点
        if (head == nullptr) {
            head = newStudent;
        }
        else {
            // 找到链表最后一个节点,将新节点插入到链表末尾
            Student* current = head;
            while (current->next) {
                current = current->next;
            }
            current->next = newStudent;
        }
    }

    void displayStudents() {
        Student* current = head;
        if (current == nullptr) {
            cout << "学生列表为空." << endl;
            return;
        }
        while (current) {
            cout << "姓名: " << current->name << ", 年龄: " << current->age << ", 专业: " << current->major << endl;
            current = current->next;
        }
    }

    void printCommonPart(StudentList list2) {
        Student* p1 = head;
        Student* p2 = list2.head;

        cout << "两个链表的公共部分: ";

        while (p1 && p2) {
            if (p1->age < p2->age) {
                p1 = p1->next;
            }
            else if (p1->age > p2->age) {
                p2 = p2->next;
            }
            else
            {
                cout << p1->age << " ";
                p1 = p1->next;
                p2 = p2->next;
            }
        }

        cout << endl;
    }
};

int main() {
    // 创建学生链表对象
    StudentList studentList1;
    // 添加学生信息
    studentList1.addStudent("小明", 10, "计算机科学");
    studentList1.addStudent("小红", 20, "数学");
    studentList1.addStudent("小李", 30, "物理");

    StudentList studentList2;
    // 添加学生信息
    studentList2.addStudent("小红", 20, "数学");
    studentList2.addStudent("大明", 40, "计算机科学");
    studentList2.addStudent("大李", 60, "物理");

    // 显示学生信息
    studentList1.displayStudents();
    studentList2.displayStudents();

    cout << "----------------------------------" << endl;
    studentList1.printCommonPart(studentList2);

    return 0;
}

4.3.3面试时链表解题的方法论

对于笔试,不用太在乎空间复杂度,一切为了时间复杂度
对于面试,时间复杂度依然放在第一位,但是一定要找到空间最省的方法

重要技巧:
额外数据结构记录(哈希表等)
快慢指针

4.3.4例3-判断一个链表是否为回文结构

【题目】给定一个单链表的头节点head,请判断该链表是否为回文结构(正念反念一样)
【例子】1->2->1,返回true;1->2->2->1,返回true;1->20->3->20->1,返回true;1->2->3,返回false
【条件】如果链表长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1)

1->2->3->2->1
笔试:
依次把各个节点放到栈中,栈弹出的顺序是链表逆序的顺序,比对,有一步不一样即不是回文结构
请添加图片描述

#include<iostream>
#include <stack>
struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(nullptr) {}
};

bool isPalindrome(ListNode* head) {
    std::stack<ListNode*> stack;//创建一个空栈 stack
    ListNode* cur = head;
    while (cur != nullptr) {//遍历整个链表,将每个节点指针都压栈,直到遍历到链表的尾节点
        stack.push(cur);
        cur = cur->next;
    }
    while (head != nullptr) {//再次遍历链表,同时从栈中弹出元素,依次与链表当前节点比较
        if (head->val != stack.top()->val) {
            return false;
        }
        stack.pop();
        head = head->next;
    }
    return true;
}
int main()
{
 // 创建一个示例链表: 1 -> 2 -> 3 -> 2 -> 1
    ListNode* node1 = new ListNode(1);
    ListNode* node2 = new ListNode(2);
    ListNode* node3 = new ListNode(3);
    ListNode* node4 = new ListNode(2);
    ListNode* node5 = new ListNode(1);
    node1->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;
    // 调用判断函数
    bool result = isPalindrome(node1);

    // 输出结果
    if (result) {
        std::cout << "该链表是回文链表" << std::endl;
    }
    else {
        std::cout << "该链表不是回文链表" << std::endl;
    }

    // 释放内存
    delete node1;
    delete node2;
    delete node3;
    delete node4;
    delete node5;
    return 0;
}

省一点空间的做法:
把一半的右边的节点放到栈中,栈弹出的和正序进行比对,有一步不一样即不是回文结构
省了1/2的空间
怎么知道放完了一半的右边的节点,一个指针达不成要求,需要两个指针,一前一后
请添加图片描述

#include<iostream>
#include<vector>
int main()
{
	std::vector<int> arr= { 1,2,3,3,1,2,1 };
	int length = sizeof(arr) / sizeof(arr[0]);
	std::vector<int>::iterator Begin = arr.begin();
	std::vector<int>::iterator End = arr.end()-1;
    bool isPalindrome = true; 
    while (Begin < End)
    {
        if (*Begin != *End)
        {
            isPalindrome = false;
            break; 
        }
        Begin++;
        End--;
    }
    if (isPalindrome)
    {
        std::cout << "true" << std::endl;
    }
    else
    {
        std::cout << "false" << std::endl;
    }
	return 0;
}

快慢指针做法:
快指针一次走两步,慢指针依次走一步,当快指针走到终点时,慢指针在中间
把一半的右边的节点放到栈中,栈弹出的和正序进行比对,有一步不一样即不是回文结构
请添加图片描述

请添加图片描述

#include<iostream>
#include <stack>
struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(nullptr) {}
};

bool isPalindrome(ListNode* head) {
    if (head == nullptr || head->next == nullptr) {
        return true;
    }
    ListNode* slow = head;
    ListNode* fast = head;

    //fast每次向后移动两个节点,直到fast达到链表的末尾或倒数第二个节点。
    while (fast->next != nullptr && fast->next->next != nullptr) {
        slow = slow->next;
        fast = fast->next->next;
    }
    //循环结束后,当快指针达到链表尾部或倒数第二个节点时循环结束,此时慢指针所指的节点即为链表的中间节点(针对奇数长度链表)或者右半部分链表的第一个节点(针对偶数长度链表)

    slow = slow->next;//将慢指针向后移动一个节点,执行右半部分链表的第一个节点
    std::stack<int> stack;//创建一个整型的栈

    //右半部分链表的节点依次入栈
    while (slow != nullptr) {
        stack.push(slow->val);
        slow = slow->next;
    }

    //比较
    ListNode* cur = head;
    while (!stack.empty() && cur != nullptr) {
        if (stack.top() != cur->val) {
            return false;
        }
        stack.pop();
        cur = cur->next;
    }
    return true;
}

int main() {
    // 创建一个示例链表: 1 -> 2 -> 3 -> 2 -> 1
    ListNode* node1 = new ListNode(1);
    ListNode* node2 = new ListNode(2);
    ListNode* node3 = new ListNode(3);
    ListNode* node4 = new ListNode(2);
    ListNode* node5 = new ListNode(1);
    node1->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;

    // 调用判断函数
    bool result = isPalindrome(node1);

    // 输出结果
    if (result) {
        std::cout << "该链表是回文链表" << std::endl;
    }
    else {
        std::cout << "该链表不是回文链表" << std::endl;
    }

    // 释放内存
    delete node1;
    delete node2;
    delete node3;
    delete node4;
    delete node5;
    return 0;
}
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if (head == nullptr || head->next == nullptr) {
            return true; // 空链表或只有一个节点的链表视为回文链表
        }

        ListNode* fast = head->next;
        ListNode* slow = head;

        // 快慢指针找到链表的中间节点
        while (fast != nullptr && fast->next != nullptr) {
            fast = fast->next->next;
            slow = slow->next;
        }

        // 反转后半部分链表
        ListNode* prev = nullptr;
        ListNode* curr = slow->next;
        while (curr != nullptr) {
            ListNode* nextNode = curr->next;
            curr->next = prev;
            prev = curr;
            curr = nextNode;
        }

        // 比较前半部分和反转后的后半部分链表
        ListNode* cur1 = head;
        ListNode* cur2 = prev;
        while (cur1 != nullptr && cur2 != nullptr) {
            if (cur1->val != cur2->val) {
                return false;
            }
            cur1 = cur1->next;
            cur2 = cur2->next;
        }

        return true;
    }
};

4.3.4.1快慢指针

如快指针一次走两步,慢指针依次走一步,当快指针走到终点时,慢指针在中间

需要熟练使用快慢指针
链表长度为1时,链表长度为2时,链表长度为3时,链表长度为小数时最特殊
情况1
1->2->3->2->1
奇数个时,当快指针走完,慢指针正好压中中间点,3
1->2->3->3->2->1
偶数个时,当快指针走完,慢指针正好压中中间点的前一个,3

情况2
1->2->3->2->1
奇数个时,当快指针走完,慢指针正好压中中间点,3
1->2->3->3->2->1
偶数个时,当快指针走完,慢指针正好压中中间点的后一个,3

情况3
1->2->3->2->1
奇数个时,当快指针走完,慢指针压中中间点前一个的位置,2
1->2->3->3->2->1
偶数个时,当快指针走完,慢指针压中中间点的前一个位置,2

4.3.5例4-将单向链表按某值划分成左边小、中间相等、右边大的形式

【题目】给定一个单链表的头节点head,节点的值类型是整型,再给定一个整数pivot。实现一个调整链表的函数,将链表调整为做部分都是值小于pivot的节点,中间部分都是值等于pivot的节点,右部分都是值大于pivot的节点
【进阶】在实现原问题功能的基础上增加如下要求
【要求】调整后所有小于pivot的节点之间的相对顺序和调整前一样
【要求】调整后所有等于pivot的节点之间的相对顺序和调整前一样
【要求】调整后所有小于pivot的节点之间的相对顺序和调整前一样
【要求】时间复杂度达到O(N),额外空间复杂度达到O(1)

笔试:
把单链表每一个节点放到数组中,选一个值做划分值,小于放左边,等于放中间,大于放右边,再重新串起来

面试:
数组中做不到相对顺序等,单链表可以做到
4->6->3->5->8->5->2->5->9
六个变量H头T尾,S小于,E等于,B大于
SH=NULL , ST=NULL , EH=NULL , ET=NULL , BH=NULL , BT=NULL

来到4节点,小于5
SH=4 , ST=4
来到6节点,大于5
BH=6 , BT=6
来到3节点,小于5, SH、ST不为空让ST指向3
SH=4 , ST=3
来到5节点,等于5
EH=5 , ET=5
来到8节点,大于5,BH、BT不为空让BT指向8
BH=6 , BT=8
来到5节点,等于5, ET不为空让ET指向5
EH=5 , ET=5
来到2节点,小于5, SH、ST不为空让ST指向2
SH=4 , ST=2
来到5节点,等于5, ET不为空让ET指向5
EH=5 , ET=5
来到9节点,大于5,BH、BT不为空让BT指向9
BH=6 , BT=9
请添加图片描述
连接
请添加图片描述
如果没有大于5的区域?如果没有等于5的区域?如果没有小于5的区域?
如果同时没有大于5的区域等于5的区域?
所以再三块重连时要讨论清楚连接
否则连上空指针会报错
请添加图片描述
请添加图片描述

4.3.6例5-复制好友随机指针节点的链表

【题目】一种特殊的单链表节点描述如下

class Node{
    int value;
    Node next;
    Node rand;
    Node(int val)
    {
        value=val;
    }
}

rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节点,也可能指向NULL。给定一个由Node节点类型组成的无环单链表的头节点head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点
【要求】时间复杂度O(N),额外空间复杂度O(1)

【解析】这个链表上每个节点由next和rand两个指针,next继续链表连接,rand不一定指向哪里
请添加图片描述
目的复制出一个一样的链表

4.3.6.1方法1-利用哈希表

用额外空间时:
可以用哈希表map,key表示老节点,value表示克隆出来的新节点
遍历,(1,1’)放到map中,(2,2’)放到map中,(3,3’)放到map中
遍历哈希表或老链表均可,下述遍历老链表的方式
遍历到老链表1,查出新链表的1‘,通过老链表2和map查出新链表2’,让1‘next到2’,通过老链表3和map查出新链表3’,让1‘rand到3’
遍历到老链表2,查出新链表的2‘,通过老链表3和map查出新链表3’,让2‘next到3’,通过老链表1和map查出新链表1’,让2‘rand到1’
遍历到老链表3,查出新链表的3‘,通过老链表NULL和map知rand NULL,让3‘next到NULL,通过老链表3和map知rand NULL,让3‘rand到NULL

返回1‘即可
请添加图片描述

4.3.6.1方法2-不利用哈希表

生成克隆节点,放在老链表老节点后,先不看rand指针
请添加图片描述
通过1节点rand找到3节点,因为克隆节点放在老链表老节点后,所以可以用3节点next找到3’节点,所以1‘rand3’
请添加图片描述
如此操作得出
请添加图片描述
之后在next方向上把新老链表分离即可
请添加图片描述
请添加图片描述

4.3.7例6-两个单链表相交的一系列问题

【题目】给定两个可能有环也可能无环的单链表,头节点head1和head2.请实现一个函数,如果两个链表相交,请返回相交的第一个节点。如果不相交,返回NULL
【要求】如果两个链表长度之和为N,时间复杂度达到O(N),额外空间复杂度到大O(1)

4.3.7.1判断环方法1哈希表

请添加图片描述
遍历一个就放入容器一个,如果是环,当count发现重复就是环,不重复就不是环,发现的这个节点即是第一个入环节点

4.3.7.2判断环方法2快慢指针

请添加图片描述
慢指针一次走一个,快指针一次走两个,如果没有环,快指针会走到空就知道了没有环,如果有环就会一直走,总会碰上
快指针和慢指针碰上后,快指针回到头,快慢指针都一次走一步,它们一定会在第一个入环节点相遇
请添加图片描述

4.3.7.3情况1都无环

请添加图片描述
左边情况必定不存在,因为这样会有一个节点有两个next了,错误
遍历第一个链表,记录头head1,尾end1,长度len1
第二个链表,记录头head2,尾end2,长度len2
先判断end1和end2内存地址是不是一个,(因为无环时必定只有上图右边情况,如果相交,最后一个节点必定两个链表共有)
如果不是,必定不相交
如果是,知head1和head2的长度,先从长的走到和短的一样的剩余长度,开始一起遍历对比,一定会共同到达第一个交点
请添加图片描述
请添加图片描述
n为链表1长度减去链表2长度的值

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if(headA==nullptr||headB==nullptr)
        {
            return nullptr;
        }
        ListNode*PA=headA;
        ListNode*PB=headB;
        int num=0;
        while(PA->next!=nullptr)
        {
            num++;
            PA=PA->next;
        }
        while(PB->next!=nullptr)
        {
            num--;
            PB=PB->next;
        }
        if(PA!=PB)
        {
            return nullptr;
        }
        PA=num>0?headA:headB;
        PB=PA==headA?headB:headA;
        num=abs(num);
        while(num!=0)
        {
            num--;
            PA=PA->next;
        }
        while(PA!=PB)
        {
            PA=PA->next;
            PB=PB->next;
        }
        return PA;
    }
};

std::abs是取绝对值

4.3.7.4情况2两个链表其中一个有环,不可能相交

没有这种情况

4.3.7.5情况3两个链表都有环

有三种情况
请添加图片描述
因为已经算出了head1和head2的入环的第一个节点loop1和loop2
所以loop1=loop2的话是第二种情况
第二种情况可以算出两个链表从头到loop1的长度
和之前无环情况1相同,让长的到达节点使两个和长度相桶,之后同时开始遍历

之后开始区分情况1和情况3,从loop1开始往下走,loop2不动,在转回到loop1之前如果能碰到loop2就是情况3,碰不到就是情况1
请添加图片描述
请添加图片描述

汇总
请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/61165.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ - 简单案例

目录 0.引用 1.Hello world 2.轮训分发消息 2.1 抽取工具类 2.2 启动两个工作线程接受消息 2.4 结果展示 3.消息应答 3.1 自动应答 3.2 手动消息应答的方法 3.3 消息自动重新入队 3.4 消息手动应答代码 4.RabbitMQ 持久化 4.1 队列如何实现持久化 4.2 消息实现持久化 5.不…

DBSCAN聚类

一、概述 DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种基于密度的聚类算法&#xff0c;簇集的划定完全由样本的聚集程度决定。聚集程度不足以构成簇落的那些样本视为噪声点&#xff0c;因此DBSCAN聚类的方式也可以用于异常点的检测。 二、算法…

【音视频SDK测评】线上K歌软件开发技术选型

摘要 在线K歌软件的开发有许多技术难点&#xff0c;需考虑到音频录制和处理、实时音频传输和同步、音频压缩和解压缩、设备兼容性问题等技术难点外&#xff0c;此外&#xff0c;开发者还应关注音乐版权问题&#xff0c;确保开发的应用合规合法。 前言 前面写了几期关于直播 …

SpringBoot+SSM实战<一>:打造高效便捷的企业级Java外卖订购系统

文章目录 项目简介项目架构功能模块管理端用户端 技术选型用户层网关层应用层数据层工具 项目优缺点结语 黑马程序员最新Java项目实战《苍穹外卖》&#xff1a;让你轻松掌握SpringBootSSM的企业级开发技巧项目简介 《苍穹外卖》是一款为餐饮企业&#xff08;餐厅、饭店&#x…

leetcode 1290.二进制链表转整数

⭐️ 题目描述 &#x1f31f; leetcode链接&#xff1a;二进制链表转整数 ps&#xff1a; 1 0 1 (0 << 1) 1 0 1 1 (1 << 1) 0 2 0 2 (2 << 1) 1 4 1 5代码&#xff1a; int getDecimalValue(struct ListNode* head) {int ans 0;while(head !…

滇医通微信小程序分析笔记

注意 本文章仅供学习交流使用&#xff0c;如果你是铁粉你就会知道博主之前发布过一篇相关的文章&#xff0c;但是由于代码涉及到法律相关所以就隐藏了&#xff0c;两年的时间过去了&#xff0c;因为女朋友已经早早安排上了&#xff0c;所以就搁置了&#xff0c;本次不做代码分…

视频监控汇聚平台EasyCVR视频分享页面WebRTC流地址播放不了是什么原因?

开源EasyDarwin视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多…

微信小程序nodejs+vue+uniapp个人家庭理财系统--论文

随着Internet的发展&#xff0c;人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化&#xff0c;网络化和电子化。网上管理&#xff0c;它将是直接管理家庭理财系统app的最新形式。本论文是以构建家庭理财系统app为目标&#xff0c;使用nodejs技术制作&…

macOS下Django环境搭建

1. macOS升级pip /Library/Developer/CommandLineTools/usr/bin/python3 -m pip install --upgrade pip 2. 卸载Python3.9.5版本 $ sudo rm -rf /usr/local/bin/python3 $ sudo rm -rf /usr/local/bin/pip3 $ sudo rm -rf /Library/Frameworks/Python.framework 3. 安装P…

人工智能大模型中token的理解

“token”是当前语言类模型的数据单位。当前的自回归语言模型是根据 token 来作为单位进行数据处理和计算&#xff0c;分词&#xff08;tokenization&#xff09;就是将句子、段落、文章这类型的长文本分解为以 token 为单位的数据结构&#xff0c;把文本分词后每个词表示成向量…

SpringBoot + ajax 实现分页和增删查改

0目录 1.SpringBoot 2.SpringBoot分页&#xff1b;增删改查 1.SpringBoot分页 创建数据库和表 创建SpringBoot工程&#xff0c;引入springboot下的分页依赖 配置application.yml 实体类 Mapper接口 Mapper.xml Service接口 Service实现类 控制层 测试 加…

并查集模板的应用:连通块

一、链接 837. 连通块中点的数量 二、题目 给定一个包含 nn 个点&#xff08;编号为 1∼n1∼n&#xff09;的无向图&#xff0c;初始时图中没有边。 现在要进行 mm 个操作&#xff0c;操作共有三种&#xff1a; C a b&#xff0c;在点 aa 和点 bb 之间连一条边&#xff0c…

【BASH】回顾与知识点梳理(五)

【BASH】回顾与知识点梳理 五 五. 数据流重导向5.1 什么是数据流重导向standard output 与 standard error output/dev/null 垃圾桶黑洞装置与特殊写法standard input &#xff1a; < 与 << 5.2 命令执行的判断依据&#xff1a; ; , &&, ||cmd ; cmd (不考虑指…

【安装】阿里云轻量服务器安装Ubuntu图形化界面(端口号/灰屏问题)

阿里云官网链接 https://help.aliyun.com/zh/simple-application-server/use-cases/use-vnc-to-build-guis-on-ubuntu-18-04-and-20-04 网上搜了很多教程&#xff0c;但是我没在界面看到有vnc连接&#xff0c;后面才发现官网有教程。 其实官网很详细了&#xff0c;不过这里还是…

针对java程序员的了解细节操作系统与进程

一、&#x1f49b; 操作系统&#xff08;浅浅概念&#xff09;&#xff1a;是用来搞管理软件的 1.对下,要管理各种硬件设备 2.对上,要给应用程序提供一个稳定的运行环境 二、&#x1f499; 进程&#xff1a;正在运行的程序&#xff0c;假如程序没有运行就不叫程序&#xff0c;…

【java安全】CommonsBeanUtils1

文章目录 【java安全】CommonsBeanUtils1前言Apache Commons BeanutilsBeanComparator如何调用BeanComparator#compare()方法&#xff1f;构造POC完整POC 调用链 【java安全】CommonsBeanUtils1 前言 在之前我们学习了java.util.PriorityQueue&#xff0c;它是java中的一个优…

echarts-pie---------3D曲状环形饼图实现!!!

示例&#xff08;参考此处饼图修改https://www.isqqw.com/viewer?id37497&#xff09; 话不多说直接上代码 此套代码可以直接再echarts官网中的此处运行 let selectedIndex ; let hoveredIndex ; option getPie3D([{name: 数学,value: 60,itemStyle: {color: #1890FF,},},{…

JVM入门到精通

一、JVM概念 1.1、什么是JVM Java Virtual Machine&#xff1a;Java虚拟机&#xff0c;用来保证Java语言跨平台 Java虚拟机可以看做是一台抽象的计算机&#xff0c;如同真实的计算机那样&#xff0c;它有自己的指令集以及各种运行时内存区域 Java虚拟机与Java语言并没有必然…

替代LT8711龙讯替代RTD2172 CS5265中文规格书4K60HZ转接线 设计Type-C转HDMI2.0高清投屏方案

龙迅LT8711是一款Type-C/DP1.2 to HDMI2.0方案芯片&#xff0c;北京集睿致远&#xff08;ASL&#xff09;推出的CS5265可以完全代替LT8711UX&#xff0c;封装尺寸比LT8711UX小的同时&#xff0c;CS5265的芯片集成度高&#xff0c;内置MCU&#xff0c;内置lLDO等&#xff0c;CS5…

并查集模板-两个操作:合并集合和查询两个元素是否属于同一个集合

一、链接 836. 合并集合 二、题目 一共有 nn 个数&#xff0c;编号是 1∼n1∼n&#xff0c;最开始每个数各自在一个集合中。 现在要进行 mm 个操作&#xff0c;操作共有两种&#xff1a; M a b&#xff0c;将编号为 aa 和 bb 的两个数所在的集合合并&#xff0c;如果两个数…