• 数据结构--单链表


    最近复习数据结构,加强下自己的基础。在复习中遇到的问题在这里做下笔记。
    单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。
    单链表的定义:

    typedef struct LNode{
        int data;               // data 中存在结点的数据域
        struct LNode *next;     // 指向后继结点的指针
    }LNode;                     // 定义单链表结点类型

    单链表的构造方法(尾插法和头插法)
    (1)尾插法

    void CreateListR(LNode *&C, int a[], int n){
        LNode *s, *r; //s 用来指向新申请结点, r 始终指向C的终端结点
        int i;
        C = (LNode *)malloc(sizeof(LNode *)); //申请C的头结点空间
        C -> next = NULL;  
        r = C; //r 指向 C 的头结点,因为此时 C 的头结点就是 C 的终端结点
        for (i=0;i<n;i++){
            s = (LNode *)malloc(sizeof(LNode *)); //s指向新申请结点
            s -> data = a[i]; 
            r -> next = s; //r 接纳新结点
            r = r -> next; //r 指向终端结点,以便接纳下一个到来的结点
        }
        r -> next = NULL; //所有元素都装入了链表 C 中,将 C 的终端结点的指针域置为NULL
    }

    尾插法

    (2)头插法

    void CreateListH(LNode *&C, int a[], int n){
        LNode *s;
        int i;
        C = (LNode *)malloc(sizeof(LNode *)); //申请C的头结点空间
        C -> next = NULL;
        for (i=0;i<n;i++){
            s = (LNode *)malloc(sizeof(LNode *)); //s指向新申请结点
            s -> data = a[i]; 
            s -> next = C -> next; //s 所指向新结点的指针域 next 指向 C 中的开始结点
            C -> next = s ; //头结点的指针域 next 指向 s 结点, 使得 s 结点成为了新的开始结点
        }
    }

    头插法

    链表删除( 删除 p->next )

    q = p -> next;
    p -> next = p -> next -> next;
    free(q);

    链表删除

    链表的插法(头插法和尾插法)和删除是所有链表知识的基础知识,很多操作都是由着三个基本操作组成。

    下面介绍一个链表的综合题目(leetCode上面的)
    给定两个非负整数(个位整数)的单链表,将两个单链表中对应元素相加的到的值保存到一个单链表中

    输入: [2, 4, 3]
    [5, 6, 4]
    输出: [7, 0 ,8]

    题目分析:(根据题目描述和输入输出实例)每个单链表中元素的值相加,如果小于10,则得到的值即为所求的值,如果相加的值大于10,那么所要求的值为个位上的数,同时十位上的1要加到下次运算中。
    思想分析:可以创建一个单链表,让其长度为输入链表中最大的长度。因为单链表没有提供length的属性,那么只有通过变量才能得到最大的长度,这样会加大运行的负担,因此在遍历单链表的时候,判断单链表是否为空,如果一个单链表为空,另一个还没有为空,那么为空的那个单链表往后的值可以认为是0,这样就不会影响最终的计算,一次遍历就可以达到最大的长度。需要注意的是,如果遍历完后,最后一次的得到的值大于10,那么需要在最后再添加一个结点,来保存得到的十位数。
    代码实现(C++)

    #include <iostream>
    using namespace std;
    
    // Definition for singly-linked list.
    struct ListNode {
        int val;
        ListNode *next;
        ListNode(int x) : val(x), next(NULL) {}
    };
    
    //使用模板定义一个函数getArrayLen,该函数将返回数组array的长度
    template <class T>
    int getArrayLen(T& array)  {
        return (sizeof(array) / sizeof(array[0]));
    }
    
    class Solution {
    public:
        /**
         *  用尾插法创建一个没有头结点的链表
         */
        ListNode* createListE(int array[], int n){
            ListNode *r, *s;
            ListNode* list = (ListNode *)malloc(sizeof(ListNode *));
            list->next = NULL;
            r = list;
            for (int i = 0; i < n; ++i) {
                s = (ListNode *)malloc(sizeof(ListNode *));
                s -> val = array[i];
                r -> next = s;
                r = r->next;
            }
            r->next = NULL;
            return list->next;
        }
    
        ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {//链表不含头结点
            int flag = 0; //定义标示符,初始化为0,链表值相加大于10为1,否则为0
            ListNode* p = l1;
            ListNode* q = l2;
            ListNode* l3 = (ListNode*)malloc(sizeof(ListNode*)); //申请l3的头结点空间(可以创建带头结点的链表,然后返回l3->next, 去除头结点即可)
            l3 -> next = NULL;
            ListNode* r = l3; //r 指向 l3 的始终最后一个结点
    
            int valp, valq; //p,q链表对应位置的值
            while (p != NULL || q != NULL){ // 一个不为空,就继续(得到最大的长度)
                valp = (p==NULL?0:p->val); // p为空的话,对应值为0;否则为p链表的值
                valq = (q==NULL?0:q->val);
    
                ListNode* s = (ListNode*)malloc(sizeof(ListNode*)); //s指向新申请结点
                if (valp + valq + flag < 10){ //小于10的情况
                    s -> val = (valp + valq + flag);
                    flag = 0; // 小于10设为0,不影响下次的计算
                }else{ //大于10的情况
                    s -> val = (valp + valq + flag) % 10; //取个位的值
                    flag = 1; // 大于10设为1,下次计算加1
                }
                //链表尾插法
                r -> next = s;
                r = r -> next;
    
                if (p != NULL) p = p -> next; //p不为空的话,向后移动一个位置
                if (q != NULL) q = q -> next;
            }
    
            if (flag == 1 ){ //最后一次结果大于10的情况
                ListNode* s = (ListNode*)malloc(sizeof(ListNode*)); //再次申请新结点
                //将新结点用尾插法插入到l3链表中
                s -> val = flag;
                r->next = s;
                r = r->next;
            }
            r -> next = NULL; //所有元素都装入了链表 l3 中,将 l3 的终端结点的指针域置为NULL
            return l3->next; //返回去除头结点的 l3 链表
        }
    };
    
    int main(int argc, const char * argv[]) {
        //测试用例
        int a[] = {2,4,3};
        int b[] = {5,6,4};
    
    //    int a[] = {3};
    //    int b[] = {7,6};
    //
    //    int a[] = {5};
    //    int b[] = {5};
    //
    //    int a[] = {3, 8};
    //    int b[] = {2};
    
        //计算数组的长度
        int alength = getArrayLen(a);
        int blength = getArrayLen(b);
    
        Solution s;
        //尾插法得到无头结点的单链表
        ListNode* l1 = s.createListE(a, alength);
        ListNode* l2 = s.createListE(b, blength);
    
        //计算两个单链表的和
        ListNode* l3 = s.addTwoNumbers(l1, l2);
        ListNode* r = l3;
        while (r!=NULL) {
            cout<<r->val<<",";
            r = r->next;
        }
        cout<<endl;
    
        return 0;
    }
    

    例题2:判断一个链表是否有环
    思路:定义两个指针,一快一慢,当两个指针相遇,则证明有环,否则没有环。
    代码如下:

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
       //找到第一次相遇的节点(如果有环,返回相遇的节点,没有环,返回NULL)
        bool cycleList(ListNode *head){
            ListNode *pSlow = head;//慢指针
            ListNode *pFast = head;//快指针
            while (pFast != NULL && pFast -> next != NULL){
                pFast = pFast -> next -> next;
                pSlow = pSlow -> next;
                if (pFast == pSlow) return true;//有环,返回true
            }
            return false;//无环 返回false
        }

    例题3:如果一个链表有环,找出环的入口
    分析:同样定义2个指针,然后将其中一个指针向后移动环的长度n,然后两个指针以相同的速度向前移动。当第二个指针指向环的入口结点时,后面的那个指针已经围绕环走了一圈,回到了入口结点,两个指针相遇。
    求环的长度,可以先找出环中任意的一个结点,然后从这个结点出发,一边向后移动,一边计数,再次返回这个结点的时候,就可以得到环的长度了。
    代码如下:

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
         //找到第一次相遇的节点(如果有环,返回相遇的节点,没有环,返回NULL)
        ListNode *meetingNode(ListNode *head){
            ListNode *pFast = head;//快指针
            ListNode *pSlow = head;//慢指针
            while(pFast != NULL && pFast -> next != NULL){
                pFast = pFast -> next -> next;
                pSlow = pSlow -> next;
                if (pFast == pSlow) return pFast;//有环,返回相遇的节点
            }
            return NULL;//无环 返回NULL
        }
    
        ListNode *detectCycle(ListNode *head) {
            ListNode *meetingLN = meetingNode(head);
            if (meetingLN == NULL) return NULL;
            //计算环的长度,找到第一次的公共顶点,然后再遍历一圈,即可确定长度
            ListNode *pHeadNode = meetingLN;
            int count = 1;
            while (pHeadNode -> next != meetingLN){
                pHeadNode = pHeadNode -> next;
                ++count;
            }
            //创建2个指针,一个指向头节点,一个向后移动环的长度,两个指针相遇的地方就是环的入口
            pHeadNode = head;
            ListNode *pLastNode = head;
            while(count){
                pLastNode = pLastNode -> next;
                --count;
            }
            while(pHeadNode != pLastNode){
                pHeadNode = pHeadNode -> next;
                pLastNode = pLastNode -> next;
            }
            return pLastNode;
        }
    不积跬步,无以至千里;不积小流,无以成江海。
  • 相关阅读:
    MFC 解析xml文件
    数字图像处理-----主成成分分析PCA
    C++设计模式——建造者模式
    总结的文章--未读
    八大排序算法
    C++读取、旋转和保存bmp图像文件编程实现
    数字图像处理-----直方图均衡化
    数字图像处理------中值滤波
    matlab图像基础知识
    MFC最大化显示任务栏
  • 原文地址:https://www.cnblogs.com/xiaocai-ios/p/7779800.html
Copyright © 2020-2023  润新知