[做题]双指针

第一天刷题。一个平实的开始,希望能坚持下来,不求波涛汹涌,大浪淘沙,但求静水流深,川流不息。

先学习双指针。题目方向分为两个:链表和数组。

在处理数组和链表相关问题时,双指针技巧是经常用到的,双指针技巧主要分为两类:左右指针快慢指针

所谓左右指针,就是两个指针相向而行或者相背而行;而所谓快慢指针,就是两个指针同向而行,一快一慢。

文章目录

      • 链表中的双指针
    • 第一道:21.合并两个有序链表
    • 第二道:分割链表
    • 第三道:合并K个升序链表
    • 第四道:单链表的倒数第 k 个节点
    • 第五道:链表的中间结点
    • 第六道:相交链表
    • 第七道:判断是否为环形链表
    • 第八道:找到环形链表的起点
      • 数组中的双指针
    • 第一道: [删除有序数组中的重复项](https://leetcode.cn/problems/remove-duplicates-from-sorted-array/)
    • 第二道:移除元素
    • 第三道:移动零
    • 第四道:最长回文子串

链表中的双指针

第一道:21.合并两个有序链表

思路一:申请第三个链表的空间,拷贝两个链表中较小的一个。

代码v1.0

class Solution {
public:
    bool comp(ListNode* l1, ListNode* l2){
        if(l1->val > l2->val) return true;
            else return false;
    }
    bool Empty_Judge(ListNode* l){
        if(l->val==0) return true;
            else return false;
    }
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if(Empty_Judge(list1)) return list2;
        if(Empty_Judge(list2)) return list1;
        ListNode* list ;
        ListNode* head = list;
        while(list1!=nullptr&&list2!=nullptr)
        {
            list = new ListNode;
            if(comp(list1,list2)){
                list->val = list2->val;
                list = list->next;
                list2 = list2->next;
            }
            else{
                list->val = list1->val;
                list = list->next;
                list1 = list1->next;
            }
        }
        while(list1!=nullptr){
            list = new ListNode(list1->val);
            list=list->next;
            list1=list1->next;
        }
        while(list2!=nullptr){
            list = new ListNode(list2->val);
            list=list->next;
            list2=list2->next;
        }
        return head;
    }

};

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

问题:返回的 head 没有任何数值。

解决:

  • 链表到下一个结点前需要空间,否则指向NULL就是野指针,也就不可能连上。

    代码开始,head 指向了没有初始化的 list ,即指向一个(指向null的)野指针,它自己也变成了野指针。如果初始化工作做好,不解决后续连接过程的“先申请空间,再连接”,就会只有一个数据。

  • 条件判断分不清 NULL 、nullptr 及其背后的 true 、false,造成代码冗余。

    Empty_judge 和 comp 函数是完完全全的多余代码,因为本可以直接在原代码中判断。

代码V2.0:

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if(!list1) return list2;
        if(!list2) return list1;
        ListNode* list = new ListNode ;
        ListNode* head = list;
        while(list1&&list2)
        {
            if(list1->val > list2->val){
                list->val = list2->val;
                list2 = list2->next;
            }
            else{
                list->val = list1->val;
                list1 = list1->next;
            }
            list->next = new ListNode;
            list = list->next;
        }

        while(list1)
        {
            list ->val = list1 -> val;
            if(list1->next) list->next = new ListNode;
            list=list->next;
            list1=list1->next;
        }
        while(list2)
        {
            list ->val = list2 -> val;
            if(list2->next) list->next = new ListNode;
            list=list->next;
            list2=list2->next;
        }

        return head;
    }

};

规避了野指针问题和相对的代码冗余问题。


思路二:在两个指针现有的空间上,只做穿针引线的工作(连接起来)

    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if(!list1) return list2;   //可以省略
        if(!list2) return list1;
        ListNode* list = new ListNode;
        ListNode* head = list;
        while(list1&&list2)
        { 
            if(list1->val > list2->val){
                list -> next = list2;
                list = list2;  //这里可以配合下面简化
                list2 = list2->next;
            }
            else{
                list -> next = list1;
                list = list1;  //简化
                list1 = list1->next;
                //list = list->next
            }
        }
        list -> next = list1 ? list1 : list2;  
        return head -> next;
    }

问题:

  • 开始的判断是否为空可以省略。
  • while 中出现重复点(只是需要一点点直觉)

解决:

看思路三。


**思路三:**labuladong ,虚拟头结点值得注意。整体与思路二没什么区别。

ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    // 虚拟头结点
    ListNode dummy = new ListNode(-1), p = dummy;
    ListNode p1 = l1, p2 = l2;
while (p1 != null && p2 != null) {
    // 比较 p1 和 p2 两个指针
    // 将值较小的的节点接到 p 指针
    if (p1.val > p2.val) {
        p.next = p2;
        p2 = p2.next;
    } else {
        p.next = p1;
        p1 = p1.next;
    }
    // p 指针不断前进
    p = p.next;
}

if (p1 != null) {
    p.next = p1;
}

if (p2 != null) {
    p.next = p2;
}

return dummy.next;

}

代码中还用到一个链表的算法题中是很常见的「虚拟头结点」技巧,也就是 dummy 节点。你可以试试,如果不使用 dummy 虚拟节点,代码会复杂很多,而有了 dummy 节点这个占位符,可以避免处理空指针的情况,降低代码的复杂性。


拓展:

思路四:递归

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
    if(l1==NULL)
        return l2;
    if(l2==NULL)
        return l1;
    if(l1->val < l2->val){
        l1->next = mergeTwoLists(l1->next,l2);
        return l1;
    }else{
        l2->next = mergeTwoLists(l1,l2->next);
        return l2;
    }
}

类似汉诺塔的思路。


第二道:分割链表

V1.0

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        //初始化
        ListNode* tail = head;
        while(tail->val < x )  tail=tail->next;
        ListNode* temp = tail;
        ListNode* maxdot = tail;//大于值开始的结点
        while(head->val > x) head = head->next;
        tail = head; //小于值开始的结点

        
        while(temp)
        {
         //<x
            if(temp->val < x){  //如果是小于值结点
                tail ->next = temp;
                tail = tail->next;
            }
            
        //>x
            if(temp->val >x &&temp->next->val < x ){
                tail->next = temp->next;
                if(temp->next->next)  temp->next = temp->next->next;
                else {
                    tail->next = temp->next;
                    temp->next = nullptr;
                    break;
                }
            }
            temp = temp->next;
        }
        tail->next = maxdot;
        return head;
    }
};

现在思路也不是很清晰

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

思路二:双头结点

ListNode partition(ListNode head, int x) {
    // 存放小于 x 的链表的虚拟头结点
    ListNode* dummy1 = new ListNode(-1);
    // 存放大于等于 x 的链表的虚拟头结点
    ListNode* dummy2 = new ListNode(-1);
    // p1, p2 指针负责生成结果链表
    ListNode* p1 = dummy1, p2 = dummy2;
    // p 负责遍历原链表,类似合并两个有序链表的逻辑
    // 这里是将一个链表分解成两个链表
    ListNode* p = head;
    while (p != null) {
        if (p.val >= x) {
            p2.next = p;
            p2 = p2.next;
        } else {
            p1.next = p;
            p1 = p1.next;
        }
        // 断开原链表中的每个节点的 next 指针
        ListNode temp = p.next;
        p.next = null;
        p = temp;
    }
    // 连接两个链表
    p1.next = dummy2.next;

    return dummy1.next;
}

创建两个头结点,先断开,后连接。

思路三:递归

class Solution {
public:
    ListNode* small = nullptr;
    pair<ListNode*, ListNode*> helper(ListNode* head, int x) {
        if (head == nullptr) return make_pair(nullptr, nullptr);
        if (head->val < x) small = head;
        pair<ListNode*, ListNode*> next = helper(head->next, x);
        if (head->val < x) {
            head->next = next.first;
            next.first = head;
        }
        else {
            head->next = next.second;
            next.second = head;
        }
        return next;
    }
    ListNode* partition(ListNode* head, int x) {
        pair<ListNode*, ListNode*> nodes = helper(head, x);
        if (nodes.first) {
            small->next = nodes.second;
            return nodes.first;
        }
        else {
            return nodes.second;
        }
    }
};

大多数链表题递归的时候可以无脑写一句,然后只要让思路从后向前进行就可以了。

cppListNode* node = func(head->next);  // func为递归函数

这里返回值选择了两个pair,first保存小于x的结点,second保存大于x的结点。

思路四:原地连接

// 1 4 3 2 5 2 
struct ListNode* partition(struct ListNode* head, int x){
    if(!head || !head->next){
        return head;
    }
    struct ListNode *p = head, *q = head, *temp = NULL;
    if(p->val < x){ 
        while(p->next->val < x){//此循环让 p 到达最远小值  1
            p = p->next; 
            if(p->next == NULL){
                return head;
            }
        }
        q = p->next;   //让 q 到达最近大值  4
    }
    else{
        p = NULL;  
    }
    while(q->next){ //q存在下一个结点
        if(q->next->val < x){  //如果最近大值下一个为小值
            temp = q->next; //temp为小值了
            q->next = temp->next;//跳过小值
            
            if(p == NULL){ //如果起始值为大值(即p 为空情况)
                temp->next = head; //头插法:第一个插入的小值连接起始大值
                head = temp;
                p = head;
            }
            else{//起始值小值(存在了)
                temp->next = p->next; //头插法:小值后
                p->next = temp;
                p = temp;
           }
        }
        else{ //为大值就串下去
            q = q->next;
        }
    }
    return head;
}

p指针指向小于 x 的值,p == null 就连接第一个大于x的值,p!=null 就按顺序进行尾插法;
(只是 p 的"尾"是 q 的"头")
q指针指向大于 x 的值,初始是第一个大于x的值, 尾插法依次往后放;
temp 服务于p的尾插法,作为中间变量。
(q 尾插循环的前面,是p,q 的初始化工作,分别指定小于x值的尾端 和 大于x值的尾端)

第三道:合并K个升序链表

思路一:遍历合并两个链表到第一个链表中去

class Solution {
public:
    ListNode* merge(ListNode* list1,ListNode* list2) //这里和合并有序链表的code一模一样
    {...}
    ListNode* mergeKLists(vector<ListNode*>& lists) {//遍历合并
        if(lists.empty()) return nullptr;
        vector<ListNode*>::iterator it = lists.begin();
        vector<ListNode*>::iterator next = lists.begin()+1;
        for(;next!=lists.end();next++){
            *it = merge(*it,*next);
        }
        return *it;
    }
};

**思路二:**labladong :配合优先队列的二叉堆,获取最小k结点

ListNode mergeKLists(ListNode[] lists) {
    if (lists.length == 0) return null;
    // 虚拟头结点
    ListNode* dummy = new ListNode(-1);
    ListNode* p = dummy;
    // 优先级队列,最小堆
    PriorityQueue<ListNode*> pq = new PriorityQueue<>(
        lists.length, (a, b)->(a.val - b.val));
    // 将 k 个链表的头结点加入最小堆
    for (ListNode head : lists) {
        if (head != null)
            pq.add(head);
    }

    while (!pq.isEmpty()) {
        // 获取最小节点,接到结果链表中
        ListNode node = pq.poll();
        p.next = node;
        if (node.next != null) {
            pq.add(node.next);
        }
        // p 指针不断前进
        p = p.next;
    }
    return dummy.next;
}

思路三:分治递归

class Solution {

    public ListNode mergeKLists(ListNode[] lists){
        if(lists.length == 0)
            return null;
        if(lists.length == 1)
            return lists[0];
        if(lists.length == 2){
           return mergeTwoLists(lists[0],lists[1]);
        }

        int mid = lists.length/2;
        ListNode[] l1 = new ListNode[mid];
        for(int i = 0; i < mid; i++){
            l1[i] = lists[i];
        }

        ListNode[] l2 = new ListNode[lists.length-mid];
        for(int i = mid,j=0; i < lists.length; i++,j++){
            l2[j] = lists[i];
        }

        return mergeTwoLists(mergeKLists(l1),mergeKLists(l2));

    }
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) return l2;
        if (l2 == null) return l1;

        ListNode head = null;
        if (l1.val <= l2.val){
            head = l1;
            head.next = mergeTwoLists(l1.next, l2);
        } else {
            head = l2;
            head.next = mergeTwoLists(l1, l2.next);
        }
        return head;
    }
}

思路四:STL中的优先队列

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        auto head = ListNode(0);
        auto comp = [](ListNode* const &a, ListNode* const &b){return a->val > b->val;};
        priority_queue<ListNode*, vector<ListNode*>, decltype(comp)> q(comp);
        for (auto &h : lists) if (h != nullptr) q.push(h);
        auto p = &head;
        while (!q.empty()) {
            p->next = q.top();
            p = p->next;
            q.pop();
            if (p->next != nullptr) q.push(p->next);
        }
        return head.next;
    }
};

第四道:单链表的倒数第 k 个节点

ListNode* removeNthFromEnd(ListNode* head, int n) {
        if(n==0||!head) return head;    //0元素 或 不动
        if(!head->next) return NULL;    //1元素 且 动
        ListNode* fformer = head;
        ListNode* former = head;
        ListNode* latter = head;
        while(--n>0) latter = latter->next;
        while(latter->next)
        {
            if(former!=head) fformer = fformer->next;
            latter = latter -> next;
            former = former -> next;
        } 
                         //1元素
        if(former == fformer) head = former->next; //2元素
        else fformer->next = former->next;   //3+元素
        return head;


第五道:链表的中间结点

    ListNode* middleNode(ListNode* head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast->next)
        {
            fast = fast->next;
            if(fast->next) fast=fast->next;
            slow = slow->next;
        }
        return slow;
    }

第六道:相交链表

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        /**
        定义两个指针, 第一轮让两个到达末尾的节点指向另一个链表的头部, 最后如果相遇则为交点(在第一轮移动中恰好抹除了长度差)
        两个指针等于移动了相同的距离, 有交点就返回, 无交点就是各走了两条指针的长度
        **/
        if(headA == null || headB == null) return null;
        ListNode pA = headA, pB = headB;
        // 在这里第一轮体现在pA和pB第一次到达尾部会移向另一链表的表头, 而第二轮体现在如果pA或pB相交就返回交点, 不相交最后就是null==null
        while(pA != pB) {
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }
        return pA;
    }

这个赋值和 条件判断 的配合非常有趣。

简短的 if 语句可用 ?来代替。

思路二:

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    int lenA = 0, lenB = 0;
    // 计算两条链表的长度
    for (ListNode p1 = headA; p1 != null; p1 = p1.next) {
        lenA++;
    }
    for (ListNode p2 = headB; p2 != null; p2 = p2.next) {
        lenB++;
    }
    // 让 p1 和 p2 到达尾部的距离相同
    ListNode p1 = headA, p2 = headB;
    if (lenA > lenB) {
        for (int i = 0; i < lenA - lenB; i++) {
            p1 = p1.next;
        }
    } else {
        for (int i = 0; i < lenB - lenA; i++) {
            p2 = p2.next;
        }
    }
    // 看两个指针是否会相同,p1 == p2 时有两种情况:
    // 1、要么是两条链表不相交,他俩同时走到尾部空指针
    // 2、要么是两条链表相交,他俩走到两条链表的相交点
    while (p1 != p2) {
        p1 = p1.next;
        p2 = p2.next;
    }
    return p1;
}

第七道:判断是否为环形链表

boolean hasCycle(ListNode head) {
    // 快慢指针初始化指向 head
    ListNode slow = head, fast = head;
    // 快指针走到末尾时停止
    while (fast != null && fast.next != null) {
        // 慢指针走一步,快指针走两步
        slow = slow.next;
        fast = fast.next.next;
        // 快慢指针相遇,说明含有环
        if (slow == fast) {
            return true;
        }
    }
    // 不包含环
    return false;
}

不解释

第八道:找到环形链表的起点

流程起始同上,先让快慢指针相遇

  • 设相遇点距离头结点为 k ,即慢指针的路程。快指针速度是慢指针二倍,路程即 k+k=2k

  • 设环形起点距离相遇点为 m , 则慢指针距离起点 k-m。巧合的是,相遇点指针不断向后走,距离起点同样为k-m

  • 所以让其中任意一个指针回到起点,经过k-m位移,另一个指针就会到达环形链表起点。

ListNode detectCycle(ListNode head) {
    ListNode fast, slow;
    fast = slow = head;
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow) break;
    }
    // 上面的代码类似 hasCycle 函数
    if (fast == null || fast.next == null) {
        // fast 遇到空指针说明没有环
        return null;
    }

    // 重新指向头结点
    slow = head;
    // 快慢指针同步前进,相交点就是环起点
    while (slow != fast) {
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
}

数组中的双指针

在数组中并没有真正意义上的指针,但我们可以把索引当做数组中的指针,这样也可以在数组中施展双指针技巧。

第一道: 删除有序数组中的重复项

基本的快慢双指针。

int removeDuplicates(int[] nums) {
    if (nums.length == 0) {
        return 0;
    }
    int slow = 0, fast = 0;
    while (fast < nums.length) {
        if (nums[fast] != nums[slow]) {
            slow++;
            // 维护 nums[0..slow] 无重复
            nums[slow] = nums[fast];
        }
        fast++;
    }
    // 数组长度为索引 + 1
    return slow + 1;
}

大同小异的一个链表题:

删除排序链表中的重复元素](https://leetcode.cn/problems/remove-duplicates-from-sorted-list/)

struct ListNode* deleteDuplicates(struct ListNode* head){
        typedef struct ListNode ListNode;
        if(!head) return head;
        ListNode* slow = head;
        ListNode* fast = head;
        while(fast)
        {
            if(fast->val!=slow->val)
            {
                slow=slow->next;
                slow->val=fast->val;
            }
            fast=fast->next;
        }
        slow->next = NULL;
        return head;

第二道:移除元素

左右指针:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int j = nums.size() - 1;
        for (int i = 0; i <= j; i++) {
            if (nums[i] == val) {
                swap(nums[i--], nums[j--]);
            }
        }
        return j + 1;
    }
};

快慢指针:

    while(fast<numsSize)
    {
        if(nums[fast]!=val)
        {
            nums[slow] = nums[fast];
            slow++;
        }
        fast++;
    }
    return slow;

扩展:保留重复k个元素的基础上删除多余元素

class Solution {
public:
 int work(vector<int>& nums, int k) {
     int len = 0;
     for(auto num : nums)
         if(len < k || nums[len-k] != num)
             nums[len++] = num;
     return len;
 }
 int removeDuplicates(vector<int>& nums) {
     return work(nums, 2);
 }
};

第三道:移动零

v1.0

    void moveZeroes(vector<int>& nums) {
        int fast = 0,slow=0;
        while(fast<nums.size())
        {
            if(nums[fast]!=0){
                nums[slow++] = nums[fast];
            }
            fast++;
        }
        while(slow<fast){
            nums[slow++]=0;
        }
    }

v2.0

        int left = 0;
        int right = 0;
        while(right < nums.size())
        {
            if(nums[right])
            {
                swap(nums[left], nums[right]);
                left++;
            }
            right++;
        }

遇到非0直接交换就可以

第四道:最长回文子串

  string longestPalindrome(string s) {
        string res = "";
        for(int i=0;i<s.size();i++)
        {
            string s1=palindrome(s,i,i);
            string s2=palindrome(s,i,i+1);
            res = s1.size()>res.size()?s1:res;
            res = s2.size()>res.size()?s2:res;
        }
        return res;
    }
    string palindrome(string s,int l,int r)
    {
        while(l>=0&&r<s.size()&&s[l]==s[r])
        {
            l--;
            r++;
        };
        return s.substr(l+1,r-l-1);
    }

左右指针,向两侧扩散。

遍历中心点。

中心点分奇数、偶数两种情况,依次比较即可。

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

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

相关文章

原生微信小程序代码转uniapp代码 插件

一、安装依赖 npm install miniprogram-to-uniapp -g 继续在命令行里&#xff0c;运行【 wtu -V 】&#xff0c;查看版本号&#xff0c;执行结果如下&#xff1a; 二、使用插件 wtu -i "这里写你的微信小程序项目路径" 如&#xff1a;【wtu -i "D:\Desktop\…

我身边很多人游戏梗懂得比我还多

我周围的很多人比我更了解游戏迷因。 各种游戏表情满天飞。 他们可以插话我玩的任何游戏。 我什至不能用锤子玩怪物猎人。 所有武器的动作我都已经熟记于心了。 是的&#xff0c;他们全程指导了我打老头带的过程&#xff1a;隐藏道具在哪里&#xff0c;哪些boss较弱&#xff0c…

【Javascript】变量和数据类型

目录 1.JavaScript介绍 内部JavaScript 外部JavaScript 内联JavaScript JavaScript输入输出语法 2.变量 2.1定义变量 2.2变量的命名规则和规范 2.3let和var区别 3.数据类型 3.1数字类型 3.2 字符串类型 3.3 布尔类型&#xff08;boolean&#xff09; 3.4 未…

Docker入门一(Docker介绍、Docker整体结构、Docker安装、镜像、容器、Docker的容器与镜像)

文章目录 一、Docker介绍1.什么是虚拟化2.虚拟化模块3.docker是什么4.docker平台介绍5.为什么使用docker6.docker主要解决的问题 二、docker整体结构1.Docker引擎介绍&#xff08;Docker Engine&#xff09;2.Docker结构概览介绍3.Docker底层技术 三、docker安装1.Docker-CE和D…

【CesiumJS-5】绘制动态路线实现飞行航线、汽车轨迹、路径漫游等

实现效果 前言 Cesium中&#xff0c;动态路线绘制的核心是借助CZML格式&#xff0c;CZML是一种用来描述动态场景的JSON数组,可以用来描述点、线、多边形、体、模型及其他图元,同时定义它们是怎样随时间变化的&#xff1b; CZML主要做三件事&#xff1a; 1.添加模型信息 2.添加…

热流道融合3D打印技术正在成为模具制造新利器

在模具领域中&#xff0c;3D打印技术与热流道技术联手&#xff0c;能迸发出更耀眼的光芒。两种技术虽然各有特点&#xff0c;但两者结合将形成互补作用&#xff0c;从而实现11&#xff1e;2”的跨越式提升。 将增材制造的灵活思维融入传统模具设计时&#xff0c;不仅能够突破传…

ubuntu下docker安装

目录 官网链接 安装步骤 docker使用方法 拉取镜像 创建镜像 运行镜像 查看运行结果 保存镜像文件 传输到windows下 官网链接 Install Docker Engine on Ubuntu | Docker Docs 安装步骤 1.运行以下命令卸载所有冲突的包&#xff1a; for pkg in docker.io docker-d…

jeecg 启动 微服务 更改配置本地host地址

127.0.0.1 jeecg-boot-redis 127.0.0.1 jeecg-boot-mysql 127.0.0.1 jeecg-boot-nacos 127.0.0.1 jeecg-boot-gateway 127.0.0.1 jeecg-boot-system 127.0.0.1 jeecg-boot-sentinel 127.0.0.1 jeecg-boot-xxljob 127.0.0.1 jeecg-boot-rabbitmq1. windows系统下&#xff0c;在开…

JAVA实战开源项目:车险自助理赔系统(Vue+SpringBoot)

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 角色管理模块2.3 车辆档案模块2.4 车辆理赔模块2.5 理赔照片模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 角色表3.2.2 车辆表3.2.3 理赔表3.2.4 理赔照片表 四、系统展示五、核心代码5.1 查询车…

网站搭建教程

网站搭建教程 一.领取一个免费域名和SSL证书&#xff0c;和CDN 1.打开网站链接&#xff1a;https://www.rainyun.com/ycpcp_ 首先创建一个CDN&#xff0c;这里以我加速域名“cdntest.biliwind.com 1”为例 这里就要填写 cdntest.biliwind.com 1 &#xff0c;而不是 https:/…

Linux学习笔记:什么是文件描述符

什么是文件描述符 C语言的文件接口文件的系统调用什么是文件描述符,文件描述符为什么是int类型?为什么新打开的文件的文件描述符不是从0开始? 文件描述符 fd (file descriptor) C语言的文件接口 当时学习C语言的时候,学习了文件接口 具体可以查看之前的文章: 链接:C语言的文…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Select)

提供下拉选择菜单&#xff0c;可以让用户在多个选项之间选择。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 Select(options: Array<SelectOption>) 参数&#xff1a;…

Autosar教程-Mcal教程-Fls配置教程

3.11.1 FLS基础知识 flash操作中有两个术语:block和page。block是flash最小的擦除单位,page则是flash写入的最小单位。以我们使用的F1KM-S4(R7F7016533)来说,它的是64 bytes, page是4bytes。这也就意味着,如果要擦除的话,最小要擦除64 bytes,但是写入可以按4字节的大小写入…

SketchUp Pro 2023 for Mac/Win:重塑设计,引领未来

在数字化浪潮席卷全球的今天&#xff0c;设计行业也迎来了前所未有的变革。SketchUp Pro 2023&#xff0c;这款专为设计师打造的草图大师软件&#xff0c;正以其强大的功能和卓越的性能&#xff0c;引领着设计界的新潮流。 SketchUp Pro 2023不仅继承了前代产品的优秀基因&…

【Java设计模式】二十三、解释器模式

文章目录 1、解释器模式2、案例 1、解释器模式 计算一个表达式的值&#xff0c;比如12-34-7&#xff0c;单纯的定义方法或函数很难适配所有&#xff0c;因为数值和运算符可以有无数种组合。 //用于n个整数相加 public static int add(Integer ... arr) {int sum 0;for (Inte…

网络学习:BGP路径属性分类

目录 前言&#xff1a; 路径属性分类 公认必遵 公认任意 可选过渡 可选非过渡 前言&#xff1a; 在默认情况下&#xff0c;到达同一目的地&#xff0c;BGP只走单条路径&#xff0c;并不会在多条路径之间执行负载均衡。对于IGP路由协议&#xff0c;当有多条路径可以到达同…

腐烂的橘子 力扣bfs

BFS用来搜索最短路径的解法是比较合适的。 比如求最少步数的解&#xff0c;最少交换次数的解&#xff0c;最快走出迷宫等等&#xff0c;因为bfs搜索过程中遇到的第一个解一定是离最初位置最近的&#xff0c;所以遇到第一个解&#xff0c;一定就是最优解&#xff0c;此时搜索算…

表单修饰符和事件修饰符

表单修饰符和事件修饰符 表单修饰符 v-model.lazy v-model.lazy 失去焦点后再收集数据 <div id"app"><textarea name"" id"" cols"30" rows"10" v-model.lazy"a"></textarea>{{a}}<textar…

水果音乐编曲和制作软件 FL Studio 中文汉化破解版下载 FL Studio 中文设置教程

FL Studio 21 也就是 Image-Line 出品的一款功能强大的编曲软件&#xff0c;全名 Fruity Loops Studio 简称“FL Studio”。 FL Studio 21.2汉化破解版是功能强大的音乐制作解决方案&#xff0c;使用旨在为用户提供一个友好完整的音乐创建环境&#xff0c;让您能够轻松创建、管…

【ansible】ansible的介绍和安装

前言运维自动化 云计算核心职能 搭建平台架构 日常运营保障 性能效率优化 相关工具 代码管理&#xff08;SCM&#xff09;&#xff1a;GitHub、GitLab、BitBucket、SubVersion 构建工具&#xff1a;maven、Ant、Gradle 自动部署&#xff1a;Capistrano、CodeDeploy 持续…