標籤: 網頁設計公司推薦

  • 2016中國國際汽車新能源及技術應用展覽會|節能與新能源汽車產業發展規劃成果展覽會

    展會時間:2016年10月13-16日

    展會地點:北京-國家會議中心

    展會定位:
    唯一的國家級新能源汽車展覽展示平臺;中國參展企業最多、展覽面積最大、展品最為豐富的節能與新能源汽車展。

    展覽規模:30,000平方米(2015年)

    觀眾數量:60,000人次(2015年)

    展覽週期:每年一屆,2013年首屆

    支援單位:中華人民共和國工業和資訊化部

    批准單位:中華人民共和國科學技術部、中國國際貿易促進委員會

    主辦單位:中國國際貿易促進委員會機械行業分會、中國電工技術學會、汽車知識雜誌社、寰球時代汽車投資管理(北京)有限公司

    合作單位:北京盛大超越國際展覽有限公司

    展位價格:
    室內光地:1280元/平方米
    標準展位:11800元/個(3m*3m)

    參展範圍:

    整車類

    純電動車,混合動力車,燃料電池車,輕型電動車,天然氣(液化氣)車,醇類及其他代用燃料車和節能汽車。

    零部件類

    電池、電機、電控等核心零部件和先進技術應用;先進內燃機、高效變速器、輕量化材料、整車優化設計及混合動力等節能技術產品。

    充電設施

    充電樁、充電機、配電櫃、充換電池及電池管理系統、停車場充電設施、智慧監控、充電站供電解決方案、充電站-智慧電網解決方案等。

    新能源汽車發展成果展示;檢測,維修,監控,實驗,安全防護裝備及媒體等.

    連絡人:岳巍 先生  
    手機(微信):135 5286 5285
    郵箱:sales2@s-expo.com  

    附:其他推薦展會

    2016上海國際汽車新能源及智慧技術展覽會
    上海車展新能源姐妹展
    展會日期:2016年6月28日-30日
    展會地點:上海新國際博覽中心
    詳情請點擊

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    南投搬家前需注意的眉眉角角,別等搬了再說!

  • 【algo&ds】2.線性表

    【algo&ds】2.線性表

    1.線性表

    線性表(英語:Linear List)是由n(n≥0)個元素()a[0],a[1],a[2]…,a[n-1]組成的。

    其中:

    • 數據元素的個數n定義為表的長度 = “list”.length() (”list”.length() = 0(表裡沒有一個元素)時稱為空表)
    • 將非空的線性表(n>=1)記作:(a[0],a[1],a[2],…,a[n-1])
    • 數據元素a[i](0≤i≤n-1)只是個抽象符號,其具體含義在不同情況下可以不同

    一個數據元素可以由若干個數據項組成。數據元素稱為記錄,含有大量記錄的線性表又稱為文件。這種結構具有下列特點:存在一個唯一的沒有前驅的(頭)數據元素;存在一個唯一的沒有後繼的(尾)數據元素;此外,每一個數據元素均有一個直接前驅和一個直接後繼數據元素。

    2.線性表的存儲結構

    • 鏈表
      • 單鏈表
        • 動態單鏈表
        • 靜態單鏈表
      • 循環鏈表
        • 單循環鏈表
        • 雙循環鏈表
      • 靜態鏈表

    3.順序表

    利用數組的連續存儲空間順序存放線性表的各元素

    3.1結構體定義

    如果需要使用自定義的結構體來維護一個順序表,通常來講結構體的元素一般是一個固定大小的數組(可用長度足夠大),以及當前數組存放的元素個數,也即數組的長度

    typedef struct LNode *List;
    struct LNode {
        ElementType Data[MAXSIZE];
        int Last;//記錄順序表的最後一個元素的下標
    } ;
    struct LNode L;
    List PtrL;

    訪問結構體的成員

    • 訪問下標為 i 的元素:L.Data[i] 或 PtrL->Data[i]
    • 線性表的長度:L.Last+1 或 PtrL->Last+1
    • 指針變量PtrL還可以這樣訪問兩個屬性(*PtrL).Data[i](*PtrL).Last,不過這種訪問方式並不常用

    而一般來講,為了簡單,不會去維護這樣一個結構體,(因為一旦維護了這個結構體,就需要去封裝相應的函數,比如說常見的插入、刪除、查找等操作),而是直接類似下面這樣

    ElementType data[MaxSize];
    int length;

    定義一個足夠大的數組,然後定義一個對應關聯的變量來時刻維護數組的長度。

    這兩種定義方式沒有什麼區別,一種是把常用操作封裝好,方便調用,另一種則是需要時刻自己維護對應的屬性。因為順序表的結構足夠簡單,所以不定義結構體也是可以的。

    3.2順序表的常見操作

    為了方便,這一節內容記錄的都是在定義的結構體基礎上,封裝的常見操作。

    1.初始化

    List MakeEmpty( ) {
        List PtrL;
        PtrL = (List )malloc( sizeof(struct LNode) );
        PtrL->Last = -1;
        return PtrL;
    }

    初始化的順序表,長度為0,所以Last為-1

    2.查找

    int Find( ElementType X, List PtrL ) {
        int i = 0;
        while( i <= PtrL->Last && PtrL->Data[i]!= X )
            i++;
        if (i > PtrL->Last) return -1; /* 如果沒找到,返回-1 */
        else return i; /* 找到后返回的是存儲位置 */
    }

    查找操作比較簡單,從順序表的第一個元素(下標為0開始)開始遍歷。

    還有一種更加巧妙一點的實現方式,就是引入哨兵思想。

    int Find( ElementType X, List PtrL ) {
        PtrL->Data[0] = x;//順序表第一個元素就是哨兵,賦值為x
        int i = PtrL->Last;//從最後一個元素開始遍歷
        while( PtrL->Data[i]!= X )
            i--;
        return i;
    }

    這樣做的好處很明顯,少了邊界的判斷,可以優化時間複雜度,編碼也更加簡單。

    注意:這裏把下標為0的元素設置為哨兵,則要求順序表從下標為1開始存儲。而且,函數如果沒有找到,則一定返回i=0

    3.插入

    看圖示應該要注意,移動的方向是從后往前移,如果從前往後移,則Data[i]=Data[i+1]=…=Data[n],因為後面的元素都被前面移過來的元素給覆蓋了。

    void Insert( ElementType X, int i, List PtrL ) {
        int j;
        if ( PtrL->Last == MAXSIZE-1 ) { /* 表空間已滿,不能插入*/
            printf("表滿");
            return;
        }
        if ( i < 1 || i > PtrL->Last+2) { /*檢查插入位置的合法性*/
            printf("位置不合法");
            return;
        }
        for ( j = PtrL->Last; j >= i-1; j-- )
            PtrL->Data[j+1] = PtrL->Data[j]; /*將 ai~ an倒序向後移動*/
        PtrL->Data[i-1] = X; /*新元素插入*/
        PtrL->Last++; /*Last仍指向最後元素*/
        return;
    }

    為什麼這裏需要判斷順序表的空間是否已滿?

    因為這個數組,是在初始化之後就固定了數組可容納的元素個數MaxSize,一旦超出,則程序下標就會越界。C++提供了動態數組vector,可以很方便的支持動態擴展數組長度,而且基本的插入刪除等操作都封裝好了,可以很方便的使用。

    4.刪除

    同樣的,需要注意元素移動的方向,如果從后往前移,則最後面的元素會一直覆蓋到Data[i]。

    void Delete( int i, List PtrL ) {
        int j;
        if( i < 1 || i > PtrL->Last+1 ) { /*檢查空表及刪除位置的合法性*/
            printf (“不存在第%d個元素”, i );
            return ;
        }
        for ( j = i; j <= PtrL->Last; j++ )
            PtrL->Data[j-1] = PtrL->Data[j]; /*將 ai+1~ an順序向前移動*/
        PtrL->Last--; /*Last仍指向最後元素*/
        return;
    }

    5.排序

    因為排序算法比較多,本文不展開講解,可以參考以下博文,內容包括了常見的十大排序算法的算法分析,時間複雜度和空間複雜度分析以及c實現和動圖圖解。

    4.鏈表

    不要求邏輯上相鄰的兩個元素物理上也相鄰;通過“鏈”建立起數據元素之間的邏輯關係。插入、刪除不需要移動數據元素,只需要修改“鏈”。

    4.1單鏈表

    typedef struct LNode *List;
    struct LNode {
        ElementType Data;
        List Next;
    };
    struct Lnode L;
    List PtrL;
    1.求表長
    int Length ( List PtrL ) {
        List p = PtrL; /* p指向表的第一個結點*/
        int j = 0;
        while ( p ) {
            p = p->Next;
            j++; /* 當前p指向的是第 j 個結點*/
        }
        return j;
    }

    時間複雜度O(n)

    2.查找

    按序查找

    List FindKth( int K, List PtrL ) {
        List p = PtrL;
        int i = 1;
        while (p !=NULL && i < K ) {
            p = p->Next;
            i++;
        }
        if ( i == K ) return p;
        /* 找到第K個,返回指針 */
        else return NULL;
        /* 否則返回空 */
    }

    時間複雜度O(n)

    按值查找

    List Find( ElementType X, List PtrL ) {
        List p = PtrL;
        while ( p!=NULL && p->Data != X )
            p = p->Next;
        return p;
    }

    時間複雜度O(n)

    3.插入

    (1)先構造一個新結點,用s指向;
    (2)再找到鏈表的第 i-1個結點,用p指向;
    (3)然後修改指針,插入結點 ( p之後插入新結點是 s)

    List Insert( ElementType X, int i, List PtrL ) {
        List p, s;
        if ( i == 1 ) { /* 新結點插入在表頭 */
            s = (List)malloc(sizeof(struct LNode)); /*申請、填裝結點*/
            s->Data = X;
            s->Next = PtrL;
            return s; /*返回新表頭指針*/
        }
        p = FindKth( i-1, PtrL ); /* 查找第i-1個結點 */
        if ( p == NULL ) { /* 第i-1個不存在,不能插入 */
            printf("參數i錯");
            return NULL;
        } else {
            s = (List)malloc(sizeof(struct LNode)); /*申請、填裝結點*/
            s->Data = X;
            s->Next = p->Next; /*新結點插入在第i-1個結點的後面*/
            p->Next = s;
            return PtrL;
        }
    }
    4.刪除

    (1)先找到鏈表的第 i-1個結點,用p指向;
    (2)再用指針s指向要被刪除的結點(p的下一個結點);
    (3)然後修改指針,刪除s所指結點;
    (4)最後釋放s所指結點的空間。

    List Delete( int i, List PtrL ) {
        List p, s;
        if ( i == 1 ) { /* 若要刪除的是表的第一個結點 */
            s = PtrL; /*s指向第1個結點*/
            if (PtrL!=NULL) PtrL = PtrL->Next; /*從鏈表中刪除*/
            else return NULL;
            free(s); /*釋放被刪除結點 */
            return PtrL;
        }
        p = FindKth( i-1, PtrL ); /*查找第i-1個結點*/
        if ( p == NULL ) {
            printf("第%d個結點不存在", i-1);
            return NULL;
        } else if ( p->Next == NULL ) {
            printf("第%d個結點不存在", i);
            return NULL;
        } else {
            s = p->Next; /*s指向第i個結點*/
            p->Next = s->Next; /*從鏈表中刪除*/
            free(s); /*釋放被刪除結點 */
            return PtrL;
        }
    }

    4.2雙鏈表

    雙向鏈表,又稱為雙鏈表,是的一種,它的每個數據結點中都有兩個,分別指向直接後繼和直接前驅。所以,從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。

    typedef struct DuLNode {
        ElemType data;
        struct DuLNode *prior, *next;
    } DuLNode, *DuLinkList;

    4.3循環鏈表

    4.3.1單循環鏈表

    存儲結構和單鏈表相同。

    typedef struct LNode {
        ElemType data;
        struct LNode *next;
    } LNode, *LinkList;
    
    // 設立尾指針的單循環鏈表的12個基本操作
    void InitList(LinkList *L) { // 操作結果:構造一個空的線性表L
        *L = (LinkList)malloc(sizeof(struct LNode)); // 產生頭結點,並使L指向此頭結點
        if (!*L) // 存儲分配失敗
            exit(OVERFLOW);
        (*L)->next = *L; // 指針域指向頭結點
    }
    
    void DestroyList(LinkList *L) { // 操作結果:銷毀線性表L
        LinkList q, p = (*L)->next; // p指向頭結點
        while (p != *L) { // 沒到表尾
            q = p->next;
            free(p);
            p = q;
        }
        free(*L);
        *L = NULL;
    }
    
    void ClearList(LinkList *L) /* 改變L */ { // 初始條件:線性表L已存在。操作結果:將L重置為空表
        LinkList p, q;
        *L = (*L)->next; // L指向頭結點
        p = (*L)->next; // p指向第一個結點
        while (p != *L) { // 沒到表尾
            q = p->next;
            free(p);
            p = q;
        }
        (*L)->next = *L; // 頭結點指針域指向自身
    }
    
    Status ListEmpty(LinkList L) { // 初始條件:線性表L已存在。操作結果:若L為空表,則返回TRUE,否則返回FALSE
        if (L->next == L) // 空
            return TRUE;
        else
            return FALSE;
    }
    
    int ListLength(LinkList L) { // 初始條件:L已存在。操作結果:返回L中數據元素個數
        int i = 0;
        LinkList p = L->next; // p指向頭結點
        while (p != L) { // 沒到表尾
            i++;
            p = p->next;
        }
        return i;
    }
    
    Status GetElem(LinkList L, int i, ElemType *e) { // 當第i個元素存在時,其值賦給e並返回OK,否則返回ERROR
        int j = 1; // 初始化,j為計數器
        LinkList p = L->next->next; // p指向第一個結點
        if (i <= 0 || i > ListLength(L)) // 第i個元素不存在
            return ERROR;
        while (j < i) { // 順指針向後查找,直到p指向第i個元素
            p = p->next;
            j++;
        }
        *e = p->data; // 取第i個元素
        return OK;
    }
    
    int LocateElem(LinkList L, ElemType e, Status(*compare)(ElemType, ElemType)) { // 初始條件:線性表L已存在,compare()是數據元素判定函數
        // 操作結果:返回L中第1個與e滿足關係compare()的數據元素的位序。
        //           若這樣的數據元素不存在,則返回值為0
        int i = 0;
        LinkList p = L->next->next; // p指向第一個結點
        while (p != L->next) {
            i++;
            if (compare(p->data, e)) // 滿足關係
                return i;
            p = p->next;
        }
        return 0;
    }
    
    Status PriorElem(LinkList L, ElemType cur_e, ElemType *pre_e) { // 初始條件:線性表L已存在
        // 操作結果:若cur_e是L的數據元素,且不是第一個,則用pre_e返回它的前驅,
        //           否則操作失敗,pre_e無定義
        LinkList q, p = L->next->next; // p指向第一個結點
        q = p->next;
        while (q != L->next) { // p沒到表尾
            if (q->data == cur_e) {
                *pre_e = p->data;
                return TRUE;
            }
            p = q;
            q = q->next;
        }
        return FALSE; // 操作失敗
    }
    
    Status NextElem(LinkList L, ElemType cur_e, ElemType *next_e) { // 初始條件:線性表L已存在
        // 操作結果:若cur_e是L的數據元素,且不是最後一個,則用next_e返回它的後繼,
        //           否則操作失敗,next_e無定義
        LinkList p = L->next->next; // p指向第一個結點
        while (p != L) { // p沒到表尾
            if (p->data == cur_e) {
                *next_e = p->next->data;
                return TRUE;
            }
            p = p->next;
        }
        return FALSE; // 操作失敗
    }
    
    Status ListInsert(LinkList *L, int i, ElemType e) /* 改變L */ { // 在L的第i個位置之前插入元素e
        LinkList p = (*L)->next, s; // p指向頭結點
        int j = 0;
        if (i <= 0 || i > ListLength(*L) + 1) // 無法在第i個元素之前插入
            return ERROR;
        while (j < i - 1) { // 尋找第i-1個結點
            p = p->next;
            j++;
        }
        s = (LinkList)malloc(sizeof(struct LNode)); // 生成新結點
        s->data = e; // 插入L中
        s->next = p->next;
        p->next = s;
        if (p == *L) // 改變尾結點
            *L = s;
        return OK;
    }
    
    Status ListDelete(LinkList *L, int i, ElemType *e) /* 改變L */ { // 刪除L的第i個元素,並由e返回其值
        LinkList p = (*L)->next, q; // p指向頭結點
        int j = 0;
        if (i <= 0 || i > ListLength(*L)) // 第i個元素不存在
            return ERROR;
        while (j < i - 1) { // 尋找第i-1個結點
            p = p->next;
            j++;
        }
        q = p->next; // q指向待刪除結點
        p->next = q->next;
        *e = q->data;
        if (*L == q) // 刪除的是表尾元素
            *L = p;
        free(q); // 釋放待刪除結點
        return OK;
    }
    
    void ListTraverse(LinkList L, void(*vi)(ElemType)) { // 初始條件:L已存在。操作結果:依次對L的每個數據元素調用函數vi()
        LinkList p = L->next->next; // p指向首元結點
        while (p != L->next) { // p不指向頭結點
            vi(p->data);
            p = p->next;
        }
        printf("\n");
    }

    4.3.2雙循環鏈表

    // 線性表的雙向鏈表存儲結構
    typedef struct DuLNode {
        ElemType data;
        struct DuLNode *prior, *next;
    } DuLNode, *DuLinkList;
    
    // 帶頭結點的雙向循環鏈表的基本操作(14個)
    void InitList(DuLinkList *L) {
        // 產生空的雙向循環鏈表L
        *L = (DuLinkList)malloc(sizeof(DuLNode));
        if (*L)
            (*L)->next = (*L)->prior = *L;
        else
            exit(OVERFLOW);
    }
    
    void DestroyList(DuLinkList *L) {
        // 操作結果:銷毀雙向循環鏈表L
        DuLinkList q, p = (*L)->next; // p指向第一個結點
        while (p != *L) { // p沒到表頭
            q = p->next;
            free(p);
            p = q;
        }
        free(*L);
        *L = NULL;
    }
    
    void ClearList(DuLinkList L) { // 不改變L
        // 初始條件:L已存在。操作結果:將L重置為空表
        DuLinkList q, p = L->next; // p指向第一個結點
        while (p != L) { // p沒到表頭
            q = p->next;
            free(p);
            p = q;
        }
        L->next = L->prior = L; // 頭結點的兩個指針域均指向自身
    }
    
    Status ListEmpty(DuLinkList L) {
        // 初始條件:線性表L已存在。操作結果:若L為空表,則返回TRUE,否則返回FALSE
        if (L->next == L && L->prior == L)
            return TRUE;
        else
            return FALSE;
    }
    
    int ListLength(DuLinkList L) {
        // 初始條件:L已存在。操作結果:返回L中數據元素個數
        int i = 0;
        DuLinkList p = L->next; // p指向第一個結點
        while (p != L) { // p沒到表頭
            i++;
            p = p->next;
        }
        return i;
    }
    
    Status GetElem(DuLinkList L, int i, ElemType *e) {
        // 當第i個元素存在時,其值賦給e並返回OK,否則返回ERROR
        int j = 1; // j為計數器
        DuLinkList p = L->next; // p指向第一個結點
        while (p != L && j < i) { // 順指針向後查找,直到p指向第i個元素或p指向頭結點
            p = p->next;
            j++;
        }
        if (p == L || j > i) // 第i個元素不存在
            return ERROR;
        *e = p->data; // 取第i個元素
        return OK;
    }
    
    int LocateElem(DuLinkList L, ElemType e, Status(*compare)(ElemType, ElemType)) {
        // 初始條件:L已存在,compare()是數據元素判定函數
        // 操作結果:返回L中第1個與e滿足關係compare()的數據元素的位序。
        // 若這樣的數據元素不存在,則返回值為0
        int i = 0;
        DuLinkList p = L->next; // p指向第1個元素
        while (p != L) {
            i++;
            if (compare(p->data, e)) // 找到這樣的數據元素
                return i;
            p = p->next;
        }
        return 0;
    }
    
    Status PriorElem(DuLinkList L, ElemType cur_e, ElemType *pre_e) {
        // 操作結果:若cur_e是L的數據元素,且不是第一個,則用pre_e返回它的前驅,
        // 否則操作失敗,pre_e無定義
        DuLinkList p = L->next->next; // p指向第2個元素
        while (p != L) { // p沒到表頭
            if (p->data == cur_e) {
                *pre_e = p->prior->data;
                return TRUE;
            }
            p = p->next;
        }
        return FALSE;
    }
    
    Status NextElem(DuLinkList L, ElemType cur_e, ElemType *next_e) {
        // 操作結果:若cur_e是L的數據元素,且不是最後一個,則用next_e返回它的後繼,
        // 否則操作失敗,next_e無定義
        DuLinkList p = L->next->next; // p指向第2個元素
        while (p != L) { // p沒到表頭
            if (p->prior->data == cur_e) {
                *next_e = p->data;
                return TRUE;
            }
            p = p->next;
        }
        return FALSE;
    }
    
    DuLinkList GetElemP(DuLinkList L, int i) { // 另加
        // 在雙向鏈表L中返回第i個元素的地址。i為0,返回頭結點的地址。若第i個元素不存在,
        // 返回NULL
        int j;
        DuLinkList p = L; // p指向頭結點
        if (i < 0 || i > ListLength(L)) // i值不合法
            return NULL;
        for (j = 1; j <= i; j++)
            p = p->next;
        return p;
    }
    
    Status ListInsert(DuLinkList L, int i, ElemType e) {
        // 在帶頭結點的雙鏈循環線性表L中第i個位置之前插入元素e,i的合法值為1≤i≤表長+1
        // 改進算法2.18,否則無法在第表長+1個結點之前插入元素
        DuLinkList p, s;
        if (i < 1 || i > ListLength(L) + 1) // i值不合法
            return ERROR;
        p = GetElemP(L, i - 1); // 在L中確定第i個元素前驅的位置指針p
        if (!p) // p=NULL,即第i個元素的前驅不存在(設頭結點為第1個元素的前驅)
            return ERROR;
        s = (DuLinkList)malloc(sizeof(DuLNode));
        if (!s)
            return OVERFLOW;
        s->data = e;
        s->prior = p; // 在第i-1個元素之後插入
        s->next = p->next;
        p->next->prior = s;
        p->next = s;
        return OK;
    }
    
    Status ListDelete(DuLinkList L, int i, ElemType *e) {
        // 刪除帶頭結點的雙鏈循環線性表L的第i個元素,i的合法值為1≤i≤表長
        DuLinkList p;
        if (i < 1) // i值不合法
            return ERROR;
        p = GetElemP(L, i); // 在L中確定第i個元素的位置指針p
        if (!p) // p = NULL,即第i個元素不存在
            return ERROR;
        *e = p->data;
        p->prior->next = p->next; // 此處並沒有考慮鏈表頭,鏈表尾
        p->next->prior = p->prior;
        free(p);
        return OK;
    }
    
    void ListTraverse(DuLinkList L, void(*visit)(ElemType)) {
        // 由雙鏈循環線性表L的頭結點出發,正序對每個數據元素調用函數visit()
        DuLinkList p = L->next; // p指向頭結點
        while (p != L) {
            visit(p->data);
            p = p->next;
        }
        printf("\n");
    }
    
    void ListTraverseBack(DuLinkList L, void(*visit)(ElemType)) {
        // 由雙鏈循環線性表L的頭結點出發,逆序對每個數據元素調用函數visit()
        DuLinkList p = L->prior; // p指向尾結點
        while (p != L) {
            visit(p->data);
            p = p->prior;
        }
        printf("\n");
    }

    4.4靜態鏈表

    前面講解的都是動態鏈表,即需要指針來建立結點之間的連接關係。而對有些問題來說結點的地址是比較小的整數(例如5位數的地址),這樣就沒有必要去建立動態鏈表,而應使用方便得多的靜態鏈表。
    靜態鏈表的實現原理是hash,即通過建立一個結構體數組,並令數組的下標直接表示結點的地址,來達到直接訪問數組中的元素就能訪問結點的效果。另外,由於結點的訪問非常方便,因此靜態鏈表是不需要頭結點的。靜態鏈表結點定義的方法如下:

    struct Node{
        typename data;//數據域
        int next;//指針域
    }node[size];

    參考資料:

    • 《算法筆記》
    • 《數據結構和算法》-極客時間專欄

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 如何給HTML標籤中的文本設置修飾線

    如何給HTML標籤中的文本設置修飾線

    text-decoration屬性介紹

    • text-decoration屬性是用來設置文本修飾線呢,text-decoration屬性一共有4個值。

    text-decoration屬性值說明表

    作用
    none 去掉文本修飾線
    underline 設置下劃線
    overline 設置上劃線
    line-through 設置刪除線

    HTML標籤自帶修飾線

    • 在開始實踐text-decoration屬性之前,筆者先給大家普及下HTML中的標籤自帶修飾線如:u標籤s標籤,若有不全大家可以在下面評論中告訴筆者,畢竟筆者也是前端的一個小白,希望和大家相互交流,互幫互助,共同進步。

    u標籤

    • 下面讓我們進入u標籤的實踐,u標籤自帶的是文本下劃線。
    • 代碼塊

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>設置文本修飾線</title>
      
    </head>
    <body>
        <u>成功不是擊敗別人,而是改變自己</u>
    </body>
    </html>
    • 結果圖

    • 注意:u標籤也可以配合HTML中的其他標籤使用,舉例:將u標籤嵌套到h1標籤中使用。

    • 代碼塊

    <h1><u>成功不是擊敗別人,而是改變自己</u></h1>

    s標籤

    • 下面讓我們進入s標籤的實踐,s標籤自帶的是文本刪除線。
    • 代碼塊

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>設置文本修飾線</title>
      
    </head>
    <body>
        <s>成功不是擊敗別人,而是改變自己</s>
    </body>
    </html>
    • 結果圖

    • 注意:s標籤也可以嵌套,和u標籤一致,筆者就不過多的介紹了。

    none去除修飾線

    • 讓我們進入text-decoration屬性的none值實踐,實踐內容如:筆者將HTML頁面中的s標籤自帶的刪除線給去除掉。

    • 代碼塊

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>設置文本修飾線</title>
        <style>
            s{
                text-decoration: none;
            }
        </style>
    </head>
    <body>
        <s>成功不是擊敗別人,而是改變自己</s>
    </body>
    </html>
    • 結果圖

    • 注意:u標籤、s標籤、包括text-decoration屬性值的所有的修飾線都可以去掉哦。

    underline設置下劃線

    • 讓我們進入text-decoration屬性的underline值實踐,實踐內容如:筆者將HTML頁面中的h2標籤中的文本設置一個下劃線。
    • 代碼塊

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>設置文本修飾線</title>
        <style>
            h2{
                text-decoration: underline;
            }
        </style>
    </head>
    <body>
        <h2>成功不是擊敗別人,而是改變自己</h2>
    </body>
    </html>
    • 結果圖

    overline設置上劃線

    • 讓我們進入text-decoration屬性的overline值實踐,實踐內容如:筆者將HTML頁面中的h2標籤中的文本設置一個上劃線。

    • 代碼塊

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>設置文本修飾線</title>
        <style>
            h2{
                text-decoration: overline;
            }
        </style>
    </head>
    <body>
        <h2>成功不是擊敗別人,而是改變自己</h2>
    </body>
    </html>
    • 結果圖

    line-through設置刪除線

    • 讓我們進入text-decoration屬性的line-through值實踐,實踐內容如:筆者將HTML頁面中的h2標籤中的文本設置一個刪除線。

    • 代碼塊

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>設置文本修飾線</title>
        <style>
            h2{
                text-decoration: line-through;
            }
        </style>
    </head>
    <body>
        <h2>成功不是擊敗別人,而是改變自己</h2>
    </body>
    </html>
    • 結果圖

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    南投搬家前需注意的眉眉角角,別等搬了再說!

  • 【集合系列】- 紅黑樹實現分析

    【集合系列】- 紅黑樹實現分析

    一、故事的起因

    JDK1.8最重要的就是引入了紅黑樹的設計(當衝突的鏈表長度超過8個的時候),為什麼要這樣設計呢?好處就是避免在最極端的情況下衝突鏈表變得很長很長,在查詢的時候,效率會非常慢。

    • 紅黑樹查詢:其訪問性能近似於折半查找,時間複雜度O(logn);
    • 鏈表查詢:這種情況下,需要遍歷全部元素才行,時間複雜度O(n);

    本文主要是講解紅黑樹的實現,只有充分理解了紅黑樹,對於後面的分析才會更加順利。

    簡單的說,紅黑樹是一種近似平衡的二叉查找樹,其主要的優點就是“平衡“,即左右子樹高度幾乎一致,以此來防止樹退化為鏈表,通過這種方式來保障查找的時間複雜度為log(n)。

    關於紅黑樹的內容,網上給出的內容非常多,主要有以下幾個特性:

    • 1、每個節點要麼是紅色,要麼是黑色,但根節點永遠是黑色的;
    • 2、每個紅色節點的兩個子節點一定都是黑色;
    • 3、紅色節點不能連續(也即是,紅色節點的孩子和父親都不能是紅色);
    • 4、從任一節點到其子樹中每個恭弘=叶 恭弘子節點的路徑都包含相同數量的黑色節點;
    • 5、所有的恭弘=叶 恭弘節點都是是黑色的(注意這裏說恭弘=叶 恭弘子節點其實是上圖中的 NIL 節點);

    在樹的結構發生改變時(插入或者刪除操作),往往會破壞上述條件3或條件4,需要通過調整使得查找樹重新滿足紅黑樹的條件。

    二、調整方式

    上面已經說到當樹的結構發生改變時,紅黑樹的條件可能被破壞,需要通過調整使得查找樹重新滿足紅黑樹的條件。

    調整可以分為兩類:一類是顏色調整,即改變某個節點的顏色,這種比較簡單,直接將節點顏色進行轉換即可;另一類是結構調整,改變檢索樹的結構關係。結構調整主要包含兩個基本操作:左旋(Rotate Left)右旋(RotateRight)

    2.1、左旋

    左旋的過程是將p的右子樹繞p逆時針旋轉,使得p的右子樹成為p的父親,同時修改相關節點的引用,使左子樹的深度加1,右子樹的深度減1,通過這種做法來調整樹的穩定性。過程如下:

    以jdk1.8為例,打開HashMap的源碼部分,紅黑樹內部類TreeNode屬性分析:

    static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
            //指向父節點的指針
            TreeNode<K,V> parent;
            //指向左孩子的指針
            TreeNode<K,V> left;
            //指向右孩子的指針
            TreeNode<K,V> right;
            //前驅指針,跟next屬性相反的指向
            TreeNode<K,V> prev;
            //是否為紅色節點
            boolean red;
            ......
    }

    左旋方法rotateLeft如下:

    /*
     * 左旋邏輯
     */
    static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                                  TreeNode<K,V> p) {
                //root:表示根節點
                //p:表示要調整的節點
                //r:表示p的右節點
                //pp:表示p的parent節點
                //rl:表示p的右孩子的左孩子節點
                TreeNode<K,V> r, pp, rl;
                //r判斷,如果r為空則旋轉沒有意義
                if (p != null && (r = p.right) != null) {
                    //多個等號的連接操作從右往左看,設置rl的父親為p
                    if ((rl = p.right = r.left) != null)
                        rl.parent = p;
                    //判斷p的父親,為空,為根節點,根節點的話就設置為黑色
                    if ((pp = r.parent = p.parent) == null)
                        (root = r).red = false;
                    //判斷p節點是左兒子還是右兒子
                    else if (pp.left == p)
                        pp.left = r;
                    else
                        pp.right = r;
                    r.left = p;
                    p.parent = r;
                }
                return root;
    }

    2.2、右旋

    了解了左旋轉之後,相應的就會有右旋,邏輯基本也是一樣,只是方向變了。右旋的過程是將p的左子樹繞p順時針旋轉,使得p的左子樹成為p的父親,同時修改相關節點的引用,使右子樹的深度加1,左子樹的深度減1,通過這種做法來調整樹的穩定性。實現過程如下:

    同樣的,右旋方法rotateRight如下:

    /*
     * 右旋邏輯
     */
    static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                                   TreeNode<K,V> p) {
                //root:表示根節點
                //p:表示要調整的節點
                //l:表示p的左節點
                //pp:表示p的parent節點
                //lr:表示p的左孩子的右孩子節點
                TreeNode<K,V> l, pp, lr;
                //l判斷,如果l為空則旋轉沒有意義
                if (p != null && (l = p.left) != null) {
                    //多個等號的連接操作從右往左看,設置lr的父親為p
                    if ((lr = p.left = l.right) != null)
                        lr.parent = p;
                    //判斷p的父親,為空,為根節點,根節點的話就設置為黑色
                    if ((pp = l.parent = p.parent) == null)
                        (root = l).red = false;
                    //判斷p節點是右兒子還是左兒子
                    else if (pp.right == p)
                        pp.right = l;
                    else
                        pp.left = l;
                    l.right = p;
                    p.parent = l;
                }
                return root;
    }

    三、操作示例介紹

    3.1、插入調整過程圖解

    3.2、刪除調整過程圖解

    3.3、查詢過程圖解

    四、總結

    至此,紅黑樹的實現就基本完成了,關於紅黑樹的結構,有很多種情況,情況也比較複雜,但是整體調整流程,基本都是先調整結構然後調整顏色,直到最後滿足紅黑樹特性要求為止。整篇文章,如果有理解不當之處,歡迎指正!

    五、參考

    1、
    2、

    作者:炸雞可樂
    出處:

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • Springboot 自動配置淺析

    Introduction

    我們知道,SpringBoot之所以強大,就是因為他提供了各種默認的配置,可以讓我們在集成各個組件的時候從各種各樣的配置文件中解放出來。

    拿一個最普通的 web 項目舉例。我們需要使用 servlet 容器,SpringBoot 就提供了嵌入式的 Tomcat 作為默認容器,不需要一行配置就能直接以最普通的 Java 程序的方式啟動:java -jar;接收請求需要一個網絡端口,默認配置好8080;處理請求需要 servlet 的多線程特性,默認配置好了最大線程數為200;處理好的請求以Restful 風格返回給調用方,SpringBoot 默認配置好了jackson進行 json 序列化,業務代碼需要做的只是返回一個 POJO 對象;連接池直接就默認配置了性能最好的的 Hikari,以及連接池的默認尺寸為10……在一個簡單的web應用中,這些配置我們甚至都可能不了解或者沒有意識到它們的存在,就可以讓程序正常運行。

    這就是自動配置的魔力——潤物細無聲。

    那麼自動配置是怎麼實現的呢?本文就從POM文件和 @SpringBootApplication 註解來簡單分析一下自動配置原理。

    真的只是簡單地,分析一下。

    POM文件

    環境

    • SpringBoot 2.0.6

    父項目

    在每一個 SpringBoot 項目一開始的 POM 文件中,就有一個父依賴

    <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.0.6.RELEASE</version>
      <relativePath/> <!-- lookup parent from repository -->
    </parent>

    點進artifactId之後來到 spring-boot-starter-parent-2.0.6.RELEASE.pom 文件,這個文件還有一個父依賴:

    <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>2.0.6.RELEASE</version>
      <relativePath>../../spring-boot-dependencies</relativePath>
    </parent>

    再繼續點進去來到 spring-boot-dependencies-2.0.6.RELEASE.pom 文件中。在該文件的 <properties> 標籤中可以看到各種各樣的組件的版本信息, <dependencyManagement> 標籤中聲明了各個組件的maven坐標信息。其中,我數了一下,在 SpringBoot 2.0.6 中一個有177個帶有版本信息的組件名,包括了大家熟悉的 elasticsearch、activemq、redis、kafka等等。

    這裏(spring-boot-dependencies-2.0.6.RELEASE.pom文件)稱為 SpringBoot 的“版本仲裁中心”,它是用來管理 SpringBoot 應用裏面的所有依賴版本的地方。 它包含了大部分開發中會用到的一些組件的版本,所以我們導入依賴默認是不需要寫版本號的,SpringBoot 會自動幫我們配置需要的版本。這樣在引入某個組件的時候不用考慮這個組件和系統中已有的組件兼不兼容之類的問題,避免了煩人的版本衝突問題。(當然,沒有在 dependencies裏面管理的依賴自然需要聲明版本號)

    啟動器(Starters)

    父項目做版本仲裁,那麼真正的 jar 包是從哪裡導入進來的呢?這就要說到我們的starters了。點開web-starter可以看到它幫我們導入了web模塊正常運行所依賴的組件,就不用我們一個一個手動導入了。

    所以,所謂的Starters就是一系列依賴描述的組合,我們可以通過導入這些starters就會有相應的依賴了並可以基於他們進行相應的開發

    Springboot把開發中中會遇到的場景都抽象成一個個的starter,比如(),只需要在項目裏面引入這些starter 相關場景的所有依賴都會導入進來。要用什麼功能就導入什麼場景的啟動器

    @SpringBootApplication

    一個典型的 springboot 項目的入口類如下所示:

    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    }

    只要運行main方法就能啟動該應用。main 中運行 run 方法時需要傳入一個類,這個類正是使用@SpringBootApplication註解標註的類,如果沒有這個註解程序是跑不起來的。

    這個註解標註在某個類上就說明這個類是 springboot 的主配置類,springboot 就應該運行這個類的 main 方法來啟動springboot應用

    點開這個註解的源碼,可以看到這是一個組合註解,最主要的是這兩個註解:

    • @SpringBootConfiguration

    • @EnableAutoConfiguration

    @SpringBootConfiguration標註在某個類上,表示這是一個 SpringBoot 的配置類,它的底層是@Configuration,屬於spring的底層註解,標註了這個註解的類表名這個類是一個配置類,取代以前開發中的xml文件配置,同時也表明這個類是 spring 容器中的組件,受容器管理。

    接下來是@EnableAutoConfiguration註解,它的用處是開啟自動配置,使用 spring 開發需要手動配置的東西,現在由 springboot 幫我們配置了,具體的實現就是通過這個註解來實現的。該組合註解有兩個。

    1. @AutoConfigurationPackage

    它的 註解中的核心代碼是 @Import({Registrar.class})

    @Import 的作用就是為容器中導入一個它指定的組件。

    Registrar 這個類中有一個方法叫registerBeanDefinitions,用於註冊一些 bean 定義信息:

    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
         AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
    }

    打個斷點在這一行代碼上,運行

    (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()

    得到的結果是主配置類所在的包名,目的就是將主配置類所在包及下面所有子包裏面的所有組件掃描到Spring容器中。同時也說明了,如果在主配置所在包的上層包添加組件的話是不會被掃描到的、不起作用的。

    2. @Import({AutoConfigurationImportSelector.class})

    該註解導入了一個自動配置的選擇器,真正地給容器中導入 springboot 自動幫我們配置好的配置類。在 AutoConfigurationImportSelector 這個選擇器中的 getAutoConfigurationEntry 方法中,以全限定類名的方式把所有需要的配置類導入 springboot 容器中。這些全限定類型所在文件路徑為:

    org/springframework/boot/spring-boot-autoconfigure/2.1.8.RELEASE/spring-boot-autoconfigure-2.1.8.RELEASE.jar!/META-INF/spring.factories

    上述兩個註解一個負責掃描我們將要加容器中的類,一個加入 springboot 為我們自動配置好的類,springboot 通過這兩種方式省略了我們大量的配置工作。

    總結

    本文從用於maven項目管理的pom.xml文件和標註在啟動類上的@SpringBootApplication註解,簡單分析了springboot 自動配置的實現。前者通過版本仲裁中心為我們維護項目組件的版本,防止依賴衝突;後者通過在加載程序的時候導入數以百計的自動配置類實現自動配置。

    原文發表於:https://pengcheng.site/2019/10/28/springboot-zi-dong-pei-zhi-qian-xi/

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    南投搬家前需注意的眉眉角角,別等搬了再說!

  • 上海推出新能源汽車充電樁綜合保險

    近期,上海保險業與國家電網積極合作,推出充電樁綜合保險產品。該產品包括充電樁財產保險和充電樁用電安全責任保險。充電樁財產保險保額最高1萬元,充電樁用電安全責任保險保額3萬元。

    合作初期,上海相關險企採取贈送方式,建立專項資金池,向新能源車主贈送推廣充電樁保險。同時也在國家電網營業廳設點,向預報樁客戶宣傳介紹充電樁綜合保險的相關產品和服務。

    據瞭解,2015年7月1日起,上海正式實施《上海市電動汽車充電基礎設施建設管理暫行規定》。7月1日後,上海市民必須提供政府備案的充電服務機構出具的“充電設施已裝證明”,才能在銷售企業辦理購買新能源汽車手續,必須安裝好充電樁後才能獲得國家和地方政府補貼、並申請到新能源車免費牌照。此外,《規定》也明確充電設施所有權人應當承擔充電設施維修更新養護及侵害第三者權益責任。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 廣西建成國首條無線供電車道 電動汽車可邊行駛邊充電

    1月19日,廣西電網公司“面向智慧電網的無線電能傳輸關鍵技術研究”項目通過驗收,宣告國內首條為電動汽車無線供電的車道建成,車輛在其上行駛,可即時、無線獲得電能,目前已投入使用。

    據瞭解,該專案是南方電網於2012年下達的科技項目,由廣西電網有限責任公司電力科學研究院負責實施,項目成功建設了國內首條電動汽車無線供電小型試驗車道,研製出國內首套輸配電線路監測終端無線供電系統。相關專家介紹,該專案攻克了電動汽車無線充電技術、無線供電技術和電能轉換3個技術難點。基於無線電能傳輸技術,電動汽車在行駛過程中,可通過車道直接給電動汽車電機供電,不需要車載電池;也可以同時為車載電池充電。目前,該項目已申請發明專利9件(均已實審,其中授權7件),授權實用新型專利20件。

    目前,該車道提供的最大功率為30千瓦,以總電池容量為24千瓦時的電動汽車為例,如果以30千瓦的功率進行充電,充滿電所需時間不足1小時,續航里程100公里。推廣使用後,該車道可以根據需求安裝任意長度,甚至可以取代車載電池,有效解決目前電動汽車電池重、電池續航力低等難題,尤其適用於固定線路電動車輛如電動公交、景區迴圈車等。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    南投搬家前需注意的眉眉角角,別等搬了再說!

  • 日問周刊 | 全棧面試匯總 | 第二期

    日問周刊 | 全棧面試匯總 | 第二期

    勤學如春起之苗,不見其增,日有所長;輟學如磨刀之石,不見其損,日有所虧。

    我在 github 上新建了一個倉庫 ,每天至少一個問題。有關全棧,graphql,devops,微服務以及軟技能,促進職業成長,歡迎交流。

    以諸葛武侯的誡子書與君共勉

    夫君子之行,靜以修身,儉以養德。非澹泊無以明志,非寧靜無以致遠。夫學須靜也,才須學也,非學無以廣才,非志無以成學。淫慢則不能勵精,險躁則不能治性。年與時馳,意與日去,遂成枯落,多不接世,悲守窮廬,將復何及!

    【Q037】linux 有哪些發行版,你最喜歡哪一個

    原文鏈接,歡迎討論:

    開放問題,不過你至少得知道一個發行版…

    【Q036】http 狀態碼中 301,302和307有什麼區別

    原文鏈接,歡迎討論:

    • 301,Moved Permanently。永久重定向,該操作比較危險,需要謹慎操作:如果設置了301,但是一段時間后又想取消,但是瀏覽器中已經有了緩存,還是會重定向。
    • 302,Fount。臨時重定向,但是會在重定向的時候改變 method: 把 POST 改成 GET,於是有了 307
    • 307,Temporary Redirect。臨時重定向,在重定向時不會改變 method

    【Q035】http 常見的狀態碼有哪些

    原文鏈接,歡迎討論:

    【Q034】如何實現一個 loading 動畫

    原文鏈接,歡迎討論:

    【Q033】如何對接口進行限流]

    原文鏈接,歡迎討論:

    一般採用漏桶算法:

    1. 漏桶初始為空
    2. API 調用是在往漏桶里注水
    3. 漏桶會以一定速率出水
    4. 水滿時 API 拒絕調用

    可以使用 redis 的計數器實現

    1. 計數器初始為空
    2. API 調用計數器增加
    3. 給計數器設置過期時間,隔段時間清零,視為一定速率出水
    4. 計數器達到上限時,拒絕調用

    當然,這隻是大致思路,這時會有兩個問題要注意

    1. 最壞情況下的限流是額定限流速率的2倍
    2. 條件競爭問題

    不過實際實現時注意以下就好了(話說一般也是調用現成的三方庫做限流…),可以參考我以前的文章

    【Q032】js 中什麼是 softbind,如何實現

    原文鏈接,歡迎討論:

    【Q031】js 中如何實現 bind

    原文鏈接,歡迎討論:

    最簡單的 bind 一行就可以實現,而在實際面試過程中也不會考察你太多的邊界條件

    Function.prototype.fakeBind = function(obj) {
      return (...args) => this.apply(obj, args)
    }

    測試一下

    function f (arg) {
      console.log(this.a, arg)
    }
    
    // output: 3, 4
    f.bind({ a: 3 })(4)
    
    // output: 3, 4
    f.fakeBind({ a: 3 })(4)

    【Q030】linux 中如何打印所有網絡接口

    原文鏈接,歡迎討論:

    ifconfig

    ifconfig 是最簡單最常用,但是打印信息太多了

    $ ifconfig

    netstat

    netstatip 也挺好用,特別是它們還可以打印路由表

    $ netstat -i

    ip

    $ ip link
    
    $ ip addr

    【Q029】websocket 如何向特定的用戶組推送消息

    redis 處維護一個對象,記錄每個 group 所對應的 connections/sockets

    {
      'Class:201901': [student1Socket, student2Socket]
    }

    當 client 剛連入 server 時,便加入某個特定的組,或者叫 room,比如 student01,剛開始連入 server,可能要加入 room:Student:01Class:201901Group:10086

    $ who
    
    $ last

    一圖勝千言

    使用 jsonb_pretty 函數,示例如下

    > select jsonb_pretty('{"a": {"b": 4}}'::jsonb)
    +----------------+
    | jsonb_pretty   |
    |----------------|
    | {              |
    |     "a": {     |
    |         "b": 4 |
    |     }          |
    | }              |
    +----------------+
    SELECT 1
    Time: 0.018s

    一個簡單的 Promise 的粗糙實現,關鍵點在於

    1. pending 時, thenable 函數由一個隊列維護
    2. 當狀態變為 resolved(fulfilled) 時,隊列中所有 thenable 函數執行
    3. resolved 時, thenable 函數直接執行

    rejected 狀態同理

    class Prom {
      static resolve (value) {
        if (value && value.then) {
          return value 
        }
        return new Prom(resolve => resolve(value))
      }
    
      constructor (fn) {
        this.value = undefined
        this.reason = undefined
        this.status = 'PENDING'
    
        // 維護一個 resolve/pending 的函數隊列
        this.resolveFns = []
        this.rejectFns = []
    
        const resolve = (value) => {
          // 注意此處的 setTimeout
          setTimeout(() => {
            this.status = 'RESOLVED'
            this.value = value
            this.resolveFns.forEach(({ fn, resolve: res, reject: rej }) => res(fn(value)))
          })
        }
    
        const reject = (e) => {
          setTimeout(() => {
            this.status = 'REJECTED'
            this.reason = e
            this.rejectFns.forEach(({ fn, resolve: res, reject: rej }) => rej(fn(e)))
          })
        }
    
        fn(resolve, reject)
      }
    
    
      then (fn) {
        if (this.status === 'RESOLVED') {
          const result = fn(this.value)
          // 需要返回一個 Promise
          // 如果狀態為 resolved,直接執行
          return Prom.resolve(result)
        }
        if (this.status === 'PENDING') {
          // 也是返回一個 Promise
          return new Prom((resolve, reject) => {
            // 推進隊列中,resolved 后統一執行
            this.resolveFns.push({ fn, resolve, reject }) 
          })
        }
      }
    
      catch (fn) {
        if (this.status === 'REJECTED') {
          const result = fn(this.value)
          return Prom.resolve(result)
        }
        if (this.status === 'PENDING') {
          return new Prom((resolve, reject) => {
            this.rejectFns.push({ fn, resolve, reject }) 
          })
        }
      }
    }
    
    Prom.resolve(10).then(o => o * 10).then(o => o + 10).then(o => {
      console.log(o)
    })
    
    return new Prom((resolve, reject) => reject('Error')).catch(e => {
      console.log('Error', e)
    })

    首參不一樣,直接上 API

    React.cloneElement(
      element,
      [props],
      [...children]
    )
    
    React.createElement(
      type,
      [props],
      [...children]
    )

    它一般可以使用第三方庫 來實現,源碼很簡單,可以讀一讀

    主要有兩個要點

    1. 選中
    2. 複製

    選中

    選中主要利用了

    選中的代碼如下

    const selection = window.getSelection();
    const range = document.createRange();
    
    range.selectNodeContents(element);
    selection.removeAllRanges();
    selection.addRange(range);
    
    selectedText = selection.toString();

    取消選中的代碼如下

    window.getSelection().removeAllRanges();

    它有現成的第三方庫可以使用:

    複製

    複製就比較簡單了,execCommand

    document.exec('copy')

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 科學警訊:熱浪使熊蜂瀕臨滅絕 連帶影響農糧產量

    環境資訊中心綜合外電;姜唯 編譯;林大利 審校

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 破紀錄降雨釀洪災 巴西交通癱瘓車輛淹沒

    摘錄自2020年2月11日公視報導

    巴西聖保羅豪雨成災,創下37年來二月份雨量最高紀錄。大雨造成交通大癱瘓,有車輛被淹沒,導致駕駛受困車頂。

    10號這一天,聖保羅的直升機、橡皮艇、竹筏等的救援任務,極為辛苦繁忙。有人見到空中吊籃離去,也只能繼續撐傘,坐車頂待救。官方宣布43所學校停課,公車僅有部分繼續營運,市區地鐵亦取消部分班次。

    聖保羅全市交通幾乎癱瘓,3小時內降下100毫米的雨量,兩條主要河流「泰特河」跟「平赫洛河」的河水溢出堤外。泰特河的水位創下15年來新高,強大水勢往低窪地區流動,街道全被淹沒,連噸位較大的大卡車都成了泡水車。國家氣象研究所指出,這場降雨已經寫下37年來二月份最多雨量。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    南投搬家前需注意的眉眉角角,別等搬了再說!