數據結構-線性表-7道算法題研究

學有所用

經過前幾天對線性表的學習,用幾道算法題對線性表的此次學習進行收尾。每一個人的思惟方式不一樣,寫的算法代碼也不一樣。如下的相關代碼,是本人感受思路比較清晰簡潔的方式。如你有本身的想法,能夠本身嘗試一下,但願能夠將你的方式分享出來給我,謝謝!算法

1.將2個遞增的有序鏈表合併爲一個鏈表的有序鏈表;要求結果鏈表仍然使用兩個鏈表的存儲空間,不另外佔用其餘的存儲空間。表中不容許有重複的數據

1.1分析已知條件

  • 兩個遞增有序鏈表list一、list2
  • 不使用額外的存儲空間
  • list3中不容許有重複數據
  • 至關於求兩個鏈表的並集

1.2思路

  • 同時遍歷兩個鏈表
  • 取出兩個鏈表中相對小的結點
  • 若是相等,取list1中的結點,釋放list2中的結點
  • 取到的結點插入到list3中(鏈表後插法)

1.3代碼實現

Status mergeOrderedLists(LinkList *list1, LinkList *list2, LinkList *list3) {
    LinkList p1 = (*list1)->next;
    LinkList p2 = (*list2)->next;
    LinkList p3 = *list1;
    *list3 = p3;
    LinkList temp;
    
    //循環遍歷兩個列表
    while (p1 && p2) {
        if (p1->data < p2->data) {//若是鏈表1中的結點data小於鏈表2中結點data,鏈表1結點插入到鏈表3中
            p3->next = p1;
            p3 = p1;
            p1 = p1->next;
        } else if (p1->data > p2->data) {//若是鏈表1中的結點data大於鏈表2中結點data,鏈表2結點插入到鏈表3中
            p3->next = p2;
            p3 = p2;
            p2 = p2->next;
        } else {//若是鏈表1中的結點data等於鏈表2中結點data,保留鏈表1的結點,free鏈表2的結點
            p3->next = p1;
            p3 = p1;
            p1 = p1->next;
            
            temp = p2->next;
            free(p2);
            p2 = temp;
        }
    }
    //退出循環的條件是鏈表1或者鏈表2同時指向NULL或者其中一個指向NULL,用一個三則運算符,把剩下的結點插入到鏈表3中。
    p3->next = p1?p1:p2;
    free(*list2);
    
    return OK;
}
int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, World!\n");
    
    LinkList list1;
    LinkList list2;
    createList(&list1);
    createList(&list2);
    for (int i = 1; i < 10; i += 2) {
        insertValue(&list1, i);
    }

    insertValue(&list2, 1);
    insertValue(&list2, 2);
    insertValue(&list2, 3);
    insertValue(&list2, 4);
    
    printf("list1:");
    displayList(list1);
    printf("\n");
    printf("list2:");
    displayList(list2);
    printf("\n");
    
    LinkList list3;
    mergeOrderedLists(&list1, &list2, &list3);
    displayList(list3);
    
    return 0;
}
複製代碼

1.4運行結果

==================分割線==================

2.已知兩個鏈表A和B分別表示兩個集合。其元素遞增排列。設計一個算法,用於求出A和B的交集,並存儲在A鏈表中;例如:La={2,4,6,8};Lb={4,6,8,10};Lc={4,6,8}

2.1分析已知條件

  • 兩個遞增有序鏈表list一、list2
  • 不佔用額外的存儲空間
  • list3中存儲兩個鏈表的交集結點
  • 至關於求兩個鏈表的交集

2.2思路

  • p1和p2指向兩個鏈表首元結點,開始遍歷。
  • p1->data == p2->data
  • 保存p1結點到list3中,釋放p2結點
  • 不相等,刪除較小的結點,並向下遍歷
  • 若是有一個鏈表遍歷到頭了,刪除另外一個鏈表的後續結點

2.3代碼實現

Status orderedListIntersection(LinkList *list1, LinkList *list2, LinkList *list3) {
    LinkList p1 = (*list1)->next;
    LinkList p2 = (*list2)->next;
    LinkList p3 = *list1;
    *list3 = p3;
    LinkList temp;
    
    while (p1 && p2) {
        if (p1->data == p2->data) {//相等,把p1加入的list3中,釋放p2。p1和p2都往下指向
            p3->next = p1;
            p3 = p1;
            p1 = p1->next;
            
            temp = p2->next;
            free(p2);
            p2 = temp;
        } else if (p1->data < p2->data){//p1小,釋放p1,p1向下指向
            temp = p1->next;
            free(p1);
            p1 = temp;
        } else {//p2小,釋放p2,p2向下指向
            temp = p2->next;
            free(p2);
            p2 = temp;
        }
    }
    
    while (p1) {//p1後面還有結點,循環刪除
        temp = p1->next;
        free(p1);
        p1 = temp;
    }
    
    while (p2) {//p2後面還有結點,循環刪除
        temp = p2->next;
        free(p2);
        p2 = temp;
    }
    
    //p3鏈表結尾設爲NULL
    p3->next = NULL;
    free(*list2);
    
    return OK;
}
int main(int argc, const char * argv[]) {
    LinkList list1;
    LinkList list2;
    
    createList(&list1);
    insertValue(&list1, 1);
    insertValue(&list1, 2);
    insertValue(&list1, 3);
    insertValue(&list1, 4);
    insertValue(&list1, 5);
    insertValue(&list1, 6);
    insertValue(&list1, 7);
    insertValue(&list1, 8);
    insertValue(&list1, 9);
    printf("list1\n");
    displayList(list1);
    
    createList(&list2);
    insertValue(&list2, 2);
    insertValue(&list2, 4);
    insertValue(&list2, 6);
    insertValue(&list2, 8);
    insertValue(&list2, 10);
    printf("list2\n");
    displayList(list2);
    
    LinkList list3;
    orderedListIntersection(&list1, &list2, &list3);
    printf("list3\n");
    displayList(list3);
    
    return 0;
}
複製代碼

2.4運行結果

==================分割線==================

3.設計一個算法,將鏈表中全部節點的連接方向」原地旋轉「,即要求僅僅利用原表的存儲空間。換句話說,要求算法空間複雜度爲O(1)。例如:L={0,2,4,6,8,10},逆轉後:L={10,8,6,4,2,0}

3.1分析已知條件

  • 原地旋轉鏈表
  • 不能使用額外的存儲空間,空間複雜度爲O(1)

3.2思路

  • 鏈表頭插法
  • p指向鏈表的首元結點,遍歷鏈表
  • 取出p指向的結點,插入到鏈表的頭結點後面

3.3代碼實現

Status listReverseOrder(LinkList *list) {
    LinkList p = (*list)->next;
    LinkList q;
    (*list)->next = NULL;
    while (p) {
        q = p->next;
        p->next = (*list)->next;
        (*list)->next = p;
        p = q;
    }
    
    return OK;
}
int main(int argc, const char * argv[]) {
    LinkList list;
    createList(&list);
    for (int i = 0; i <= 10; i+=2) {
        insertValue(&list, i);
    }
    displayList(list);
    
    listReverseOrder(&list);
    displayList(list);
    
    return 0;
}
複製代碼

3.4運行結果

==================分割線==================

4.設計一個算法,刪除遞增有序鏈表中值大於等於mink且小於等於maxk(mink,maxk是給定的兩個參數,其值能夠和表中的元素相同,也能夠不一樣)的全部元素

4.1分析已知條件

  • 遞增有序鏈表
  • 刪除mink-maxk之間的值,包含等於

4.2思路

  • 找最小值的前一個結點
  • 找到最大值的結點
  • 循環刪除找到的範圍內的結點

4.3代碼實現

Status OrderedListDelElemByRange(LinkList *list, ElemType min, ElemType max) {
    if (*list == NULL) {
        return ERROR;
    }
    
    LinkList p = (*list)->next;
    LinkList low = NULL, high = NULL;
    //找到小於min的結點的前一個結點low
    while (p && p->data < min) {
        low = p;
        p = p->next;
    }
    //找到小於等於max的結點high
    while (p && p->data <= max) {
        p = p->next;
    }
    high = p;
    //原鏈表剔除low->next到high之間的結點
    low->next = high;
    
    //刪除low->next到high之間的結點
    LinkList temp;
    while (low != high) {
        temp = low->next;
        free(low);
        low = temp;
    }
    
    return OK;
}

int main(int argc, const char * argv[]) {
    LinkList list;
    createList(&list);
    for (int i = 1; i < 100; i++) {
        insertValue(&list, i);
    }
    displayList(list);
    
    OrderedListDelElemByRange(&list, 8, 24);
    displayList(list);
    
    return 0;
}
複製代碼

4.4運行結果

==================分割線==================

5.設將n(n>1)個整數存放到一維數組R中,試設計一個在時間和空間兩方面都儘量高效的算法;將R中保存的序列左移p個位置(0<p<n)個位置,即將R中的數據由(x0,x1,......,xn-1)變換爲(xp,xp+1,......,xn-1,x0,x1,...xp-1)。例如:pre[10] = {0,1,2,3,4,5,6,7,8,9},n=10,p=3;pre[10]={3,4,5,6,7,8,9,0,1,2}

5.1分析已知條件

  • 是數組,不是鏈表
  • 時間和空間兩個方面儘量高效
  • 數組循環左移

5.2思路

  • 倒敘原數組{9,8,7,6,5,4,3,2,1,0}
  • 找到分割點,分割數組{(9,8,7,6,5,4,3),(2,1,0)}
  • 再分別倒敘分割點的前部分和後部分{(3,4,5,6,7,8,9),(0,1,2)}

5.3代碼實現

void reverse(int *arr,int left ,int right){
    int low = left;
    int high = right;
    while (low < high) {
        arr[low] ^= arr[high] ^= arr[low] ^= arr[high];
        low ++;
        high --;
    }
}

void leftShift(int *arr, int n, int p){
    if (p > 0 && p < n) {
        //總體翻轉
        reverse(arr, 0, n - 1);
        //翻轉分割點前面部分
        reverse(arr, 0, n - p - 1);
        //翻轉分割點後面部分
        reverse(arr, n-p, n-1);
    }
}
int main(int argc, const char * argv[]) {
    int arr[10] = {0,1,2,3,4,5,6,7,8,9};
    for (int i = 0; i < 10; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    leftShift(arr, 10, 3);
    for (int i = 0; i < 10; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}
複製代碼

5.4運行結果

==================分割線==================

6.已知一個整數序列A = (a0,a1,a2,...an-1),其中(0<= ai <=n),(0<= i<=n). 若存在ap1= ap2 = ...= apm = x,且m>n/2(0<=pk<n,1<=k<=m),則稱x 爲 A的主元素. 例如,A = (0,5,5,3,5,7,5,5),則5是主元素; 若B = (0,5,5,3,5,1,5,7),則A 中沒有主元素,假設A中的n個元素保存在一個一維數組中,請設計一個儘量高效的算法,找出數組元素中的主元素,若存在主元素則輸出該元素,不然輸出-1.

6.1分析已知條件

  • 找主元素
  • 元素出現的次數大於數組長度的一半

6.2思路

  • 先找候選主元素
  • key = 第一個元素, count = 1
  • 循環數組
  • 下一個元素與key相等,count++
  • 下一個元素與key不相等,而且count > 0,count
  • 若是count < 0,key = 當前元素,count = 1
  • 若是count > 0
  • 循環找這個元素出現的次數,用count表示
  • count大於數組的length/2 返回key 不然返回-1

6.3代碼實現

int getMainElement(int *arr, int n){
    //把第一個元素當作候選主元素
    int key = arr[0];
    int count = 1;
    for (int i = 1; i < n; i++) {
        if (key == arr[i]) {
            count ++;
        } else {
            if (count > 0) {
                count --;
            } else {
                key = arr[i];
                count = 1;
            }
        }
    }
    
    //count > 0 ,統計候選主元素出現的次數,用count表示
    if (count > 0) {
        for (int i = count = 0; i < n; i++) {
            if (arr[i] == key) {
                count ++;
            }
        }
    }
    
    //判斷count是否知足大於數組一半的要求
    if (count > n / 2) {
        return key;
    } else {
        return -1;
    }
}
int main(int argc, const char * argv[]) {
    int a[] = {0,5,5,3,5,7,5,5};
    int b[] = {0,5,5,3,5,1,5,7};
    int c[] = {0,1,2,3,4,5,6,7};
    for (int i = 0; i < 8; i++) {
        printf("%d ", a[i]);
    }
    printf("\n");
    int value = getMainElement(a, 8);
    printf("value = %d \n", value);
    
    value = getMainElement(b, 8);
    printf("value = %d \n", value);
    
    value = getMainElement(c, 8);
    printf("value = %d \n", value);
    
    return 0;
}
複製代碼

6.4運行結果

==================分割線==================

7.用單鏈表保存m個整數, 結點的結構爲(data,link),且|data|<=n(n爲正整數). 如今要去設計一個時間複雜度儘量高效的算法. 對於鏈表中的data 絕對值相等的結點, 僅保留第一次出現的結點,而刪除其他絕對值相等的結點.例如,鏈表A = {21,-15,15,-7,15}, 刪除後的鏈表A={21,-15,-7};

7.1分析已知條件

  • 時間複雜度儘量高的算法
  • 空間換時間
  • 申請一個空間大小爲n+1(0號單元不使用)的輔助數組
  • 數組中保存鏈表中出現的值
  • 這樣,一次循環就能夠達到目的

7.2思路

  • 申請大小爲n+1的輔助數組
  • 遍歷鏈表
  • 若數組中 |data| = 0 說明第一次出現 並賦值1
  • 若不等於 0 說明已經出現過,刪除結點

7.3代碼實現

void listDelRepeatElem(LinkList *list,int n){
    //開闢輔助數組
    int *arr = alloca(sizeof(int)*n);
    for (int i = 0; i < n; i++) {
        *(arr+i) = 0;
    }
    
    LinkList p = (*list)->next;
    LinkList temp = *list;
    
    //遍歷鏈表
    while (p) {
        //若是該絕對值已經在結點上出現過,則刪除該結點
        if (arr[abs(p->data)] == 1) {
            temp->next = p->next;
            free(p);
            p = temp->next;
        } else {//未出現過的結點,則將數組中對應位置置爲1
            arr[abs(p->data)] = 1;
            temp = p;
            p = p->next;
        }
    }
}
int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, World!\n");
    
    LinkList list;
    createList(&list);
    insertValue(&list, 21);
    insertValue(&list, -15);
    insertValue(&list, 15);
    insertValue(&list, -7);
    insertValue(&list, 15);
    displayList(list);
    listDelRepeatElem(&list, 21);
    displayList(list);
    
    return 0;
}
複製代碼

7.4運行結果

==================分割線==================

總結

線性表告一段落,接下來還會繼續學習數據結構相關知識。繼續享受學習過程的快樂。記住:沿途的風景要比目的地更彎的否!!!數組

相關文章
相關標籤/搜索