數(shù)據(jù)結(jié)構(gòu)與算法總結(jié)

1. 鏈表

鏈表是最基本的數(shù)據(jù)結(jié)構(gòu),面試官也常常用鏈表來考察面試者的基本能力幸乒,而且鏈表相關(guān)的操作相對(duì)而言比較簡(jiǎn)單懦底,也適合考察寫代碼的能力。鏈表的操作也離不開指針罕扎,指針又很容易導(dǎo)致出錯(cuò)聚唐。綜合多方面的原因,鏈表題目在面試中占據(jù)著很重要的地位腔召。

鏈表問題中的一個(gè)重要的方法叫雙指針法杆查。定義兩個(gè)指針,一個(gè)叫慢指針臀蛛,另一個(gè)叫快指針亲桦。通常慢指針每次向前移動(dòng)一個(gè)節(jié)點(diǎn),而快指針每次向前移動(dòng)若干個(gè)節(jié)點(diǎn)浊仆。這個(gè)方法通常用于尋找鏈表中特定的位置客峭。比如找到鏈表的中點(diǎn),可以讓快指針每次移動(dòng)兩個(gè)節(jié)點(diǎn)抡柿。這樣當(dāng)快指針到達(dá)鏈表末尾時(shí)舔琅,慢指針剛好在鏈表中間的位置。

鏈表結(jié)點(diǎn)聲明如下:

struct ListNode
{
    int value;
    ListNode * next;
    ListNode(int x) : value(x), next(NULL) {}
};

1.1 求單鏈表中結(jié)點(diǎn)的個(gè)數(shù)

這是最最基本的了沙绝,應(yīng)該能夠迅速寫出正確的代碼搏明,注意檢查鏈表是否為空。時(shí)間復(fù)雜度為O(n)闪檬。

int GetListLenght(ListNode* head)
{
    if(head == NULL)    return 0;
    ListNode* current = head;
    int len = 0;
    while(current != NULL)
    {
        len++;
        current = current->next;
    }
    return len;
}

1.2 將單鏈表反轉(zhuǎn)

從頭到尾遍歷原鏈表星著,每遍歷一個(gè)結(jié)點(diǎn),將其摘下放在新鏈表的最前端粗悯。注意鏈表為空和只有一個(gè)結(jié)點(diǎn)的情況虚循。時(shí)間復(fù)雜度為O(n)。

ListNode* ReverseList(ListNode* head)
{
    if(head == NULL || head->next == NULL)    return head;
    ListNode* reverseHead = NULL;
    ListNode* current = head;
    while(current != NULL)
    {
        ListNode* temp = current;
        current = current->next;
        temp->next = reverseHead;
        reverseHead = temp;
    }
    return reverseHead;
}

1.3 查找單鏈表中的倒數(shù)第K個(gè)結(jié)點(diǎn)(k>0)

使用兩個(gè)指針,先讓前面的指針走到正向第k個(gè)結(jié)點(diǎn)横缔,這樣前后兩個(gè)指針的距離差是k-1铺遂,之后前后兩個(gè)指針一起向前走,前面的指針走到最后一個(gè)結(jié)點(diǎn)時(shí)茎刚,后面指針?biāo)附Y(jié)點(diǎn)就是倒數(shù)第k個(gè)結(jié)點(diǎn)襟锐。

ListNode * RGetKthNode(ListNode * head, unsigned int k)
{
    if(head == NULL)    return NULL;
    ListNode* aHead = head, behind = head;
    while(k>1)
    {
        if(aHead == NULL)    return NULL;
        aHead = aHead->next;
        k--;
    }
    while(aHead->next != NULL)
    {
        behind = behind->next;
        aHead = aHead->next;
    }
    return behind;
}

1.4 判斷一個(gè)單鏈表中是否有環(huán)

如果一個(gè)鏈表中有環(huán),也就是說用一個(gè)指針去遍歷膛锭,是永遠(yuǎn)走不到頭的粮坞。因此,我們可以用兩個(gè)指針去遍歷初狰,一個(gè)指針一次走兩步莫杈,一個(gè)指針一次走一步,如果有環(huán)奢入,兩個(gè)指針肯定會(huì)在環(huán)中相遇筝闹。時(shí)間復(fù)雜度為O(n)。

bool HasCycle(ListNode* head)
{
    ListNode* fast = head,* slow = head;
    while(fast != NULL && fast->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)    return true;
    }
    return false;
}

1.5 判斷一個(gè)單鏈表中是否有環(huán)腥光,如果存在关顷,求進(jìn)入環(huán)中的第一個(gè)節(jié)點(diǎn)

這題不光要求出是否有環(huán),而且還需要得到這個(gè)環(huán)開始的節(jié)點(diǎn)柴我。譬如下面這個(gè)解寝,起點(diǎn)就是n2扩然。

        n6-----------n5
        |              |
n1------n2----n3----n4|

我們?nèi)匀豢梢允褂脙蓚€(gè)指針fast和slow艘儒,fast走兩步,slow走一步夫偶,判斷是否有環(huán)界睁,當(dāng)有環(huán)重合之后,譬如上面在n5重合了兵拢,那么如何得到n2呢翻斟?
首先我們知道,fast每次比slow多走一步说铃,所以重合的時(shí)候访惜,fast移動(dòng)的距離是slot的兩倍,我們假設(shè)n1到n2距離為a腻扇,n2到n5距離為b债热,n5到n2距離為c,fast走動(dòng)距離為a+ b+c+b幼苛,而slow為 a+b窒篱,有方程a+b+c+b=2x(a+b),可以知道a=c,所以我們只需要在重合之后墙杯,一個(gè)指針從n1,而另一個(gè)指針從n5,都每次走一步揍障,那么就可以在n2重合了啤月。

ListNode* DetectCycle(ListNode* head)
{
    ListNode *slow = head, *fast = head;
    while(fast != NULL && fast->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            ListNode* slow2 = head;
            while(slow2 != slow)
            {
                slow2 = slow2->next;
                slow = slow->next;
            }
            return slow2;
        }
    }
    return NULL;
}

1.6 合并兩個(gè)排好序的鏈表

利用附加頭結(jié)點(diǎn)簡(jiǎn)化操作

ListNode* MergeTowList(ListNode* head1, ListNode* head2)
{
    ListNode* dummy(0);
    ListNode* temp = &dummy;
    while(head1 != NULL && head2 != NULL)
    {
        int val1 = head1->value;
        int val2 = head2->value;
        if(val1 < val2)
        {
            temp->next = head1;
            temp = head1;
            head1 = head1->next;
        }
        else
        {
            temp->next = head2;
            temp = head2;
            head2 = head2->next;
        }
    }
    if(head1 != NULL)    temp->next = head1;
    else if(head2 != NULL)    temp->next = head2; 
    return dummy.next;
}

1.7 去除有序鏈表中的重復(fù)元素

ListNode *DeleteDuplicates(ListNode *head) {
    if (head == nullptr) return nullptr;
    for (ListNode *prev = head, *cur = head->next; cur; cur = prev->next) {
        if (prev->val == cur->val) {
            prev->next = cur->next;
            delete cur;
        } 
        else {
            prev = cur;
        }
    }
    return head;
}

1.8 判斷兩個(gè)單鏈表是否相交

如果兩個(gè)鏈表相交于某一節(jié)點(diǎn),那么在這個(gè)相交節(jié)點(diǎn)之后的所有節(jié)點(diǎn)都是兩個(gè)鏈表所共有的嫉髓。也就是說低滩,如果兩個(gè)鏈表相交,那么最后一個(gè)節(jié)點(diǎn)肯定是共有的岩喷。先遍歷第一個(gè)鏈表恕沫,記住最后一個(gè)節(jié)點(diǎn),然后遍歷第二個(gè)鏈表纱意,到最后一個(gè)節(jié)點(diǎn)時(shí)和第一個(gè)鏈表的最后一個(gè)節(jié)點(diǎn)做比較婶溯,如果相同,則相交偷霉,否則不相交迄委。時(shí)間復(fù)雜度為O(len1+len2),因?yàn)橹恍枰粋€(gè)額外指針保存最后一個(gè)節(jié)點(diǎn)地址类少,空間復(fù)雜度為O(1)叙身。參考代碼如下:

bool IsIntersected(ListNode* head1, ListNode* head2)
{
    if(head1 == NULL || head2 == NULL)    return false;
    ListNode* tail1 = head1;
    while(tail1->next != NULL)    tail1 = tail1->next;
    ListNode* tail2 = head2;
    while(tail2->next != NULL)    tail2 = tail2->next;
    return tail1 == tail2;
}

1.9 求兩個(gè)單鏈表相交的第一個(gè)節(jié)點(diǎn)

對(duì)第一個(gè)鏈表遍歷,計(jì)算長(zhǎng)度len1硫狞,同時(shí)保存最后一個(gè)節(jié)點(diǎn)的地址信轿。對(duì)第二個(gè)鏈表遍歷,計(jì)算長(zhǎng)度len2残吩,同時(shí)檢查最后一個(gè)節(jié)點(diǎn)是否和第一個(gè)鏈表的最后一個(gè)節(jié)點(diǎn)相同财忽,若不相同,不相交泣侮,結(jié)束即彪。

兩個(gè)鏈表均從頭節(jié)點(diǎn)開始,假設(shè)len1大于len2活尊,那么將第一個(gè)鏈表先遍歷len1-len2個(gè)節(jié)點(diǎn)隶校,此時(shí)兩個(gè)鏈表當(dāng)前節(jié)點(diǎn)到第一個(gè)相交節(jié)點(diǎn)的距離就相等了,然后一起向后遍歷蛹锰,知道兩個(gè)節(jié)點(diǎn)的地址相同深胳。時(shí)間復(fù)雜度,O(len1+len2)宁仔。參考代碼如下:

ListNode* FindFirstCommonNode( ListNode *head1, ListNode *head2) {
    if(head1 == NULL && head2 == NULL)    return NULL;
    int len1=0,len2=0;
    ListNode* list1 = head1;
    ListNode* list2 = head2;
    while(list1 != NULL){
        list1 = list1->next;
        len1++;
    }
    while(list2 != NULL){
        list2 = list2->next;
        len2++;
    }
    
    int interval;
    ListNode* longList;
    ListNode* shortList;
    if(len1>=len2){
        interval = len1-len2;
        longList = head1;
        shortList = head2;
    }
    else{
        interval = len2-len1;
        longList = head2;
        shortList = head1;
    }
    
    for(int i=0;i<interval;i++)    longList = longList->next;
    
    while(longList != NULL && shortList != NULL && longList != shortList){
        longList = longList->next;
        shortList = shortList->next;
    }
    
    return longList;
}

2. 二叉樹

樹是一種比較重要的數(shù)據(jù)結(jié)構(gòu)稠屠,尤其是二叉樹峦睡。二叉樹是一種特殊的樹,在二叉樹中每個(gè)節(jié)點(diǎn)最多有兩個(gè)子節(jié)點(diǎn)权埠,一般稱為左子節(jié)點(diǎn)和右子節(jié)點(diǎn)(或左孩子和右孩子)榨了,并且二叉樹的子樹有左右之分,其次序不能任意顛倒攘蔽。二叉樹是遞歸定義的龙屉,因此,與二叉樹有關(guān)的題目基本都可以用遞歸思想解決满俗,當(dāng)然有些題目非遞歸解法也應(yīng)該掌握转捕,如非遞歸遍歷節(jié)點(diǎn)等等。

二叉樹節(jié)點(diǎn)定義如下:

struct BinaryTreeNode
{
    int value;
    BinaryTreeNode* left;
    BinaryTreeNode* right;
    BinaryTreeNode(int x) : value(x), left(NULL), right(NULL) {}
};

2.1 求二叉樹中的結(jié)點(diǎn)個(gè)數(shù)

遞歸解法:

  • 如果二叉樹為空唆垃,節(jié)點(diǎn)個(gè)數(shù)為0
  • 如果二叉樹不為空五芝,二叉樹節(jié)點(diǎn)個(gè)數(shù) = 左子樹節(jié)點(diǎn)個(gè)數(shù) + 右子樹節(jié)點(diǎn)個(gè)數(shù) + 1
int GetNodeNumber(BinaryTreeNode* root)
{
    if(root == NULL)    return 0;
    return GetNodeNumber(root->left) + GetNodeNumber(root->right) + 1;
}

2.2 求二叉樹的深度

遞歸解法:

  • 如果二叉樹為空,二叉樹的深度為0
  • 如果二叉樹不為空辕万,二叉樹的深度 = max(左子樹深度枢步, 右子樹深度) + 1
int GetDeepth(BinaryTreeNode* root)
{
    if(root == NULL)    return 0;
    int depthLeft = GetDeepth(root->left);
    int depthRight = GetDeepth(root->right);
    return depthLeft > depthRight ? (depthLeft + 1) : (depthRight + 1);
}

2.3 前序遍歷,中序遍歷渐尿,后序遍歷

前序遍歷遞歸解法:如果二叉樹為空醉途,空操作;如果二叉樹不為空砖茸,訪問根節(jié)點(diǎn)隘擎,前序遍歷左子樹,前序遍歷右子樹凉夯。中序遍歷遞歸解法:如果二叉樹為空货葬,空操作;如果二叉樹不為空恍涂,中序遍歷左子樹宝惰,訪問根節(jié)點(diǎn),中序遍歷右子樹再沧。后序遍歷遞歸解法:如果二叉樹為空,空操作尊残;如果二叉樹不為空炒瘸,后序遍歷左子樹,后序遍歷右子樹寝衫,訪問根節(jié)點(diǎn)顷扩。

void preOrderTraverse(BinaryTreeNode* root)
{
    if(root == NULL)    return;
    visit(root);
    PreOrderTraverse(root->left); // 前序遍歷左子樹  
    PreOrderTraverse(root->right); // 前序遍歷右子樹
}

void InOrderTraverse(BinaryTreeNode* root)
{
    if(root == NULL)    return;
    InOrderTraverse(root->left); // 中序遍歷左子樹 
    visit(root); // 訪問根節(jié)點(diǎn) 
    InOrderTraverse(root->right); // 中序遍歷右子樹  
}

void PostOrderTraverse(BinaryTreeNode * root)  
{  
    if(root == NULL)    return;  
    PostOrderTraverse(root->left); // 后序遍歷左子樹  
    PostOrderTraverse(root->right); // 后序遍歷右子樹  
    Visit(root); // 訪問根節(jié)點(diǎn)  
} 

2.4 層次遍歷

相當(dāng)于廣度優(yōu)先搜索,使用隊(duì)列實(shí)現(xiàn)慰毅。隊(duì)列初始化隘截,將根節(jié)點(diǎn)壓入隊(duì)列。當(dāng)隊(duì)列不為空,進(jìn)行如下操作:彈出一個(gè)節(jié)點(diǎn)婶芭,訪問东臀,若左子節(jié)點(diǎn)或右子節(jié)點(diǎn)不為空,將其壓入隊(duì)列犀农。

void LevelTraverse(BinaryTreeNode* root)
{
    if(root == NULL) return ;
    queue<BinaryTreeNode*> que;
    que.push(root);
    while(!que.empty())
    {
        BinaryTreeNode* tempNode = que.front();
        que.pop();
        visit(tempNode);
        if(root->left != NULL)    que.push(tempNode->left);
        if(root->right != NULL)    que.push(tempNode->right);
    }
    return ;
}

2.5 求二叉樹第K層的節(jié)點(diǎn)個(gè)數(shù)

遞歸解法:

  • 如果二叉樹為空或者k<1返回0
  • 如果二叉樹不為空并且k==1惰赋,返回1
  • 如果二叉樹不為空且k>1,返回左子樹中k-1層的節(jié)點(diǎn)個(gè)數(shù)與右子樹k-1層節(jié)點(diǎn)個(gè)數(shù)之和
int GetNodeNumKthLevel(BinaryTreeNode* root, int k)
{
    if(root == NULL || k<1)    return 0;
    if(k == 1)    return 1;
    int numLeft = GetNodeNumKthLevel(root->left, k-1);
    int numRight = GetNodeNumKthLevel(root->right,k-1);
    return numLeft + numRight;
}

2.6 求二叉樹中葉子節(jié)點(diǎn)的個(gè)數(shù)

遞歸解法:

  • 如果二叉樹為空呵哨,返回0赁濒;
  • 如果二叉樹不為空且左右子樹為空,返回1孟害;
  • 如果二叉樹不為空拒炎,且左右子樹不同時(shí)為空,返回左子樹中葉子節(jié)點(diǎn)個(gè)數(shù)加上右子樹中葉子節(jié)點(diǎn)個(gè)數(shù)挨务。
int GetLeafNodeNum(BinaryTreeNode * root)  
{  
    if(root == NULL)  return 0;  
    if(root->left == NULL && root->right == NULL)  return 1;  
    int numLeft = GetLeafNodeNum(root->left); // 左子樹中葉節(jié)點(diǎn)的個(gè)數(shù)  
    int numRight = GetLeafNodeNum(root->right); // 右子樹中葉節(jié)點(diǎn)的個(gè)數(shù)  
    return (numLeft + numRight);  
}

2.7 判斷兩棵二叉樹是否結(jié)構(gòu)相同

不考慮數(shù)據(jù)內(nèi)容枝冀。結(jié)構(gòu)相同意味著對(duì)應(yīng)的左子樹和對(duì)應(yīng)的右子樹都結(jié)構(gòu)相同。遞歸解法如下:

  • 如果兩棵二叉樹都為空耘子,返回真
  • 如果兩棵二叉樹一棵為空果漾,另一棵不為空,返回假
  • 如果兩棵二叉樹都不為空谷誓,如果對(duì)應(yīng)的左子樹和右子樹都同構(gòu)返回真绒障,其他返回假
bool StructureCmp(BinaryTreeNode * root1, BinaryTreeNode * root2)  
{  
    if(root1 == NULL && root2 == NULL) // 都為空,返回真  
        return true;  
    else if(root1 == NULL || root2 == NULL) // 只有一個(gè)為空捍歪,返回假  
        return false;  
    bool resultLeft = StructureCmp(root1->left, root2->left); // 比較對(duì)應(yīng)左子樹   
    bool resultRight = StructureCmp(root1->right, root2->right); // 比較對(duì)應(yīng)右子樹  
    return (resultLeft && resultRight);  
} 

2.8 判斷二叉樹是不是平衡二叉樹

平衡二叉樹:
遞歸解法:

  • 如果二叉樹為空户辱,返回真
  • 如果二叉樹不為空,如果左子樹和右子樹都是AVL樹并且左子樹和右子樹高度相差不大于1糙臼,返回真庐镐,其他返回假
bool IsAVL(BinaryTreeNode * root, int & height)  
{  
    if(root == NULL) // 空樹,返回真  
    {  
        height = 0;  
        return true;  
    }  
    int heightLeft;  
    bool resultLeft = IsAVL(root->left, heightLeft);  
    int heightRight;  
    bool resultRight = IsAVL(root->right, heightRight);  
    // 左子樹和右子樹都是AVL变逃,并且高度相差不大于1必逆,返回真  
    if(resultLeft && resultRight && abs(heightLeft - heightRight) <= 1) 
    {  
        height = max(heightLeft, heightRight) + 1;  
        return true;  
    }  
    else  
    {  
        height = max(heightLeft, heightRight) + 1;  
        return false;  
    }  
}

2.9 求二叉樹的鏡像

遞歸解法:

  • 如果二叉樹為空,返回空
  • 如果二叉樹不為空揽乱,求左子樹和右子樹的鏡像名眉,然后交換左子樹和右子樹
BinaryTreeNode* Mirror(BinaryTreeNode* root)
{
    if(root == NULL)    return NULL;
    BinaryTreeNode* mLeft = Mirror(root->left); // 求左子樹鏡像
    BinaryTreeNode* mRight = Mirror(root->right); // 求右子樹鏡像
    // 交換左子樹和右子樹
    root->left = mRight;
    root->right = mLeft;
    return root;
}

2.10 迭代的遍歷算法

//利用棧進(jìn)行遞歸前序遍歷
void PreorderTraversal(BinaryTreeNode* root)
{
    stack<const BinaryTreeNode*> s;
    if(root != NULL)    s.push(root);
    while(!s.empty())
    {
        const BinaryTreeNode* temp = s.top();
        s.pop();
        visit(temp);
        if(temp->right != NULL) s.push(temp->right);
        if(temp->left != NULL) s.push(temp->left);
    }
}

void InOrderTraversal(BinaryTreeNode* root)
{
    stack<const BinaryTreeNode*> s;
    const BinaryTreeNode* temp = root;
    while(!s.empty() || temp !=NULL)
    {
        if(temp != NULL){
            s.push(temp);
            temp = temp->left;
        }
        else{
            temp = s.top();
            s.pop();
            visit(temp);
            temp = temp->right;
        }
    }
}

void PostOrderTraversal(BinaryTreeNode* root)
{
    stack<const BinaryTreeNode*> s;
    const BinaryTreeNode* temp = root, * pre = NULL;
    do{
        while(temp != NULL){
            s.push(temp);
            temp = temp->left;
        }
        pre = NULL;
        while(!s.empty()){
            temp = s.top();
            s.pop();
            //如果該結(jié)點(diǎn)的右子節(jié)點(diǎn)不存在或已被訪問,就訪問它
            if(temp->right == pre){
                visit(temp);
                pre = temp;
            }
            else{
                s.push(temp);
                temp = temp->right;
                break;
            }
        }
    }while(!s.empty());
}

2.11 判斷是否存在一條從root到其中一個(gè)葉子節(jié)點(diǎn)的路徑的和等于給定的值

直接使用深度優(yōu)先遍歷求解

bool DFS(int target, int sum, BinaryTreeNode* root)
{
    if(root == NULL)    return false;
    sum += root->value;
    if(root->left == NULL && root->right == NULL)
    {
        if(sum == target)    return true;
        else return false;
    }
    bool leftPart = DFS(target,sum,root->left);
    bool rightPart = DFS(target,sum,root->right);
    return leftPart || rightPart;
}

bool hasPathSum(BinaryTreeNode* root,int sum)
{
    if(root == NULL)    return false;
    return DFS(sum,0,root);
}

3. 字符串操作與算法

3.1 字符串匹配算法

3.1.1 樸素的匹配算法

就是所謂的暴力匹配凰棉。假設(shè)文本T長(zhǎng)度為m损拢,模式串P長(zhǎng)度為n,算法從文本第1位從左向右開始與模式串P進(jìn)行匹配撒犀,無(wú)論是否匹配成功福压,模式串都后移1位開始繼續(xù)進(jìn)行重新匹配掏秩,總共進(jìn)行m-n+1次匹配。算法極其簡(jiǎn)單荆姆,因此效率極其有限蒙幻,時(shí)間復(fù)雜度為O(m*n),故不常被用胞枕。

void NaiveMatch(const char* T,const char* P)
{
    int m = strlen(T);
    int n = strlen(P);
    for(int i=0;i<m-n;i++){
        int j;
        for(j=0;j<n;j++)
            if(T[i+j] != P[j])    break;
        if(j==n)    cout<<i<<" ";
    }
    cout<<endl;
}

3.1.2 Horspool算法

int* HorspoolTable(const char *P)  
{  
    int n = strlen(P);  
    int *ht = new int[256];  
    for (int i=0; i<256; i++)  
        ht[i] = n;  
    for (int i=0; i<n-1; i++)  
        ht[(int)P[i]] = n - i - 1;  
    return ht;  
}  
  
void Horspool(const char *T, const char *P)  
{  
    int m = strlen(T);  
    int n = strlen(P);  
    int *ht = HorspoolTable(P);  
  
    int i=0, k=n-1;  
    while (i<=m-n)  
    {  
        for (k=n-1; k>=0 && T[i+k]==P[k]; k--) ;  
  
        if (k==-1)  
            cout<<i<<" ";  
        i = i + ht[(int)T[i+n-1]];  
    }  
    cout<<endl;  
    delete []ht;  
}  

3.2 將字符串轉(zhuǎn)換為整數(shù)

需考慮各種條件杆煞,如空字符,正負(fù)號(hào)腐泻,非法輸入等問題决乎,還要考慮超出int范圍的問題。下面的代碼中派桩,若是非法輸入构诚,則輸出0,也沒考慮超出范圍的問題铆惑。若要考慮非法輸入范嘱,可設(shè)置一個(gè)全局變量。

int StrToInt(string s)
{
    int result = 0;
    //判斷是否為空字符串
    if(s.empty())    return result;
    int flag = 0;
    for(int i=0;i<s.size();i++){
        if(i == 0){
            //正負(fù)號(hào)判斷
            if(s[i] == '+')    flag = 1;
            else if(s[i] == '-')    flag = -1;
            else if(s[i] >= '0' && s[i] <= '9'){
                result = s[i] - '0';
                flag = 1;
            }
            else break;
        }
        else{
            if(s[i] >= '0' && s[i] <= '9')
                result = result*10 + (s[i] - '0');
            //非法輸入返回0
            else{
                result = 0;
                break;
            }
        }
    }
    return flag*result;
}

4. 排序算法總結(jié)

4.1 冒泡排序

基本思想:每次比較兩個(gè)相鄰的元素员魏,如果他們的順序錯(cuò)誤就把他們交換過來丑蛤。

時(shí)間復(fù)雜度

  • 最好的情況下,待排序記錄已經(jīng)有序撕阎,只需要一趟排序就可以完成受裹,所以冒泡排序的最好時(shí)間復(fù)雜度為 O(n)。
  • 最壞的情況下虏束,待排序記錄反序棉饶,這時(shí)需要 n - 1 趟排序,每趟排序需要比較 n - i 次比較操作镇匀,這時(shí)比較和移動(dòng)的次數(shù)達(dá)到最大值照藻,所以冒泡排序的最壞時(shí)間復(fù)雜度為 O(n ^ 2)。
  • 平均時(shí)間復(fù)雜度為 O(n ^ 2)汗侵。因?yàn)樗苿?dòng)的次數(shù)較多幸缕,其平均時(shí)間性能還不如直接插入排序。
void bubble_sort(int s[],int n)
{
    for (int i=0;i<n;i++)
    {
        for (int j=n-1;j>i;j--)
        {
            if (s[j-1]>s[j])
            {
                swap(s[j-1],s[j]);
            }
        }
    }
}

冒泡排序的優(yōu)化:在算法中增加一個(gè)標(biāo)志exchange晃择,用以標(biāo)識(shí)本趟冒泡結(jié)果是否發(fā)生了交換冀值;如果沒有發(fā)生交換則exchange=false,表示全部元素已經(jīng)排好宫屠,因而可以結(jié)束算法;如果exchange=true滑蚯,表示本趟有元素發(fā)生交換浪蹂,還需執(zhí)行下一趟排序抵栈。

void new_bubble_sort(int s[],int n)
{
    bool exchange;
    for (int i=0;i<n;i++)
    {
        exchange = false;
        for (int j=n-1;j>i;j--)
        {
            if (s[j-1]>s[j])
            {
                swap(s[j-1],s[j]);
                exchange =true;
            }
        }
        if(exchange == false)   return;
    }
}

4.2 插入排序

基本思想:每次將一個(gè)待排序元素按其大小插入到前面已經(jīng)排好的序列中的適當(dāng)位置,直至元素全部插入為止

時(shí)間復(fù)雜度分析: 直接插入排序算法主要進(jìn)行有兩個(gè)操作坤次,查找比較古劲,移動(dòng)記錄。這兩個(gè)操作均和記錄長(zhǎng)度n相關(guān)缰猴。其平均時(shí)間復(fù)雜度為O(n^2)产艾。這在排序算法里面算慢的,但是當(dāng)記錄較少時(shí)滑绒,它的效率還是可以不錯(cuò)的闷堡。

空間復(fù)雜度分析:直接插入排序只需要一個(gè)元素的輔助空間,用于元素的位置交換O(1)疑故。

直接插入排序是穩(wěn)定排序杠览。它在元素基本有序的情況下(接近最好情況),比較和移動(dòng)的次數(shù)都較少纵势,效率是很高的踱阿。

4.2.1 直接插入排序

void insert_sort(int s[],int n)
{
    for (int i=1;i<n;i++)
    {
        if (s[i]<s[i-1])
        {
            int temp = s[i], j=i-1;
            do 
            {
                s[j+1] = s[j];
                j--;
            } 
            while (j>=0 && temp<s[j]);
            s[j+1] = temp;
        }
    }
}

4.2.2 二分插入排序

利用二分查找,查找待插入元素應(yīng)插入前面有序序列的位置钦铁。

4.3 選擇排序

4.3 直接選擇排序

基本思想:每次(第i次)在后面待排序元素中選出排序碼最小的元素软舌,作為有序元素序列的第i個(gè)元素。待n-2趟作完牛曹,待排序元素只剩下1個(gè)佛点,就不用排序了。

時(shí)間復(fù)雜度分析:無(wú)論待排序記錄的初始狀態(tài)如何躏仇,在第i趟排序中選出最小關(guān)鍵字的記錄恋脚,需做n-i次比較,因此焰手,總的比較次數(shù)為:n*(n-1) /2=O(n^2)糟描,當(dāng)待排序記錄的初始狀態(tài)為正序時(shí),移動(dòng)次數(shù)為 0书妻;當(dāng)初始狀態(tài)為反序時(shí)船响,每趟排序均要執(zhí)行交換操作,總的移動(dòng)次數(shù)取最大值3 *(n-1)躲履。所以见间,直接選擇排序的平均時(shí)間復(fù)雜度為O(n^2)

非穩(wěn)定排序工猜。

void select_sort(int s[],int left,int right)
{
    for (int i=left;i<right;i++)
    {
        int min = i;
        for (int j=i+1;j<=right;j++)
        {
            if(s[j]<s[min]) min = j;
        }
        if(min != i)    swap(s[i],s[min]);
    }
}

4.3.2 堆排序

堆實(shí)際上是一棵完全二叉樹米诉,對(duì)于最大堆,其任何一非葉節(jié)點(diǎn)的關(guān)鍵字不小于其左右子節(jié)點(diǎn)的關(guān)鍵字篷帅。堆排序就是利用大最大堆堆頂記錄的是最大關(guān)鍵字這一特性史侣,使得每次從無(wú)序中選擇最大記錄變得簡(jiǎn)單拴泌。其基本思想為:

  • 將初始待排序關(guān)鍵字序列(R1,R2....Rn)構(gòu)建成最大堆,此堆為初始的無(wú)序區(qū)惊橱;
  • 將堆頂元素R[1]與最后一個(gè)元素R[n]交換蚪腐,此時(shí)得到新的無(wú)序區(qū)(R1,R2,......Rn-1)和新的有序區(qū)(Rn),且滿足R[1,2...n-1]<=R[n];
  • 由于交換后新的堆頂R[1]可能違反堆的性質(zhì),因此需要對(duì)當(dāng)前無(wú)序區(qū)(R1,R2,......Rn-1)調(diào)整為新堆税朴,然后再次將R[1]與無(wú)序區(qū)最后一個(gè)元素交換回季,得到新的無(wú)序區(qū)(R1,R2....Rn-2)和新的有序區(qū)(Rn-1,Rn)。不斷重復(fù)此過程直到有序區(qū)的元素個(gè)數(shù)為n-1正林,則整個(gè)排序過程完成泡一。

堆排序是采用的二叉堆進(jìn)行排序的,二叉堆就是一棵二叉樹卓囚,它需要遍歷的次數(shù)就是二叉樹的深度瘾杭,而根據(jù)完全二叉樹的定義,它的深度至少是lg(N+1)哪亿。最多是多少呢粥烁?由于二叉堆是完全二叉樹,因此蝇棉,它的深度最多也不會(huì)超過lg(2N)讨阻。因此,遍歷一趟的時(shí)間復(fù)雜度是O(N)篡殷,而遍歷次數(shù)介于lg(N+1)和lg(2N)之間钝吮;因此得出它的時(shí)間復(fù)雜度是O(N*lgN)。

4.4 快速排序

時(shí)間復(fù)雜度分析

  • 最壞情況:每次劃分選取的基準(zhǔn)都是當(dāng)前無(wú)序區(qū)中關(guān)鍵字最小(或最大)的記錄板辽,劃分的結(jié)果是基準(zhǔn)左邊的子區(qū)間為空(或右邊的子區(qū)間為空)奇瘦,而劃分所得的另一個(gè)非空的子區(qū)間中記錄數(shù)目,僅僅比劃分前的無(wú)序區(qū)中記錄個(gè)數(shù)減少一個(gè)劲弦。此時(shí)耳标,時(shí)間復(fù)雜度為 O(n ^ 2)。
  • 最好情況:每次劃分所取的基準(zhǔn)都是當(dāng)前無(wú)序區(qū)的"中值"記錄邑跪,劃分的結(jié)果是基準(zhǔn)的左次坡、右兩個(gè)無(wú)序子區(qū)間的長(zhǎng)度大致相等』總的關(guān)鍵字比較次數(shù):0(nlgn)砸琅。
  • 盡管快速排序的最壞時(shí)間為 O(n ^ 2),但就平均性能而言轴踱,它是基于關(guān)鍵字比較的內(nèi)部排序算法中速度最快者症脂,快速排序亦因此而得名。它的平均時(shí)間復(fù)雜度為 O(nlgn)。

空間復(fù)雜度分析:快速排序需要一個(gè)棧來實(shí)現(xiàn)遞歸摊腋。若每次劃分較為均勻(也就是對(duì)半分沸版,基準(zhǔn)值總是中值)嘁傀,則其遞歸樹的高度為O(lgn)兴蒸,故遞歸后需棧空間為O(lgn)细办。最壞情況下橙凳,遞歸樹的高度為O(n),所需的椥ψ玻空間為O(n)岛啸。

int partition(int s[],int low,int high)
{
    int key = s[high];
    int piv = low;
    for (int i=low;i<high;i++)
    {
        if (s[i]<=key)
        {
            swap(s[piv],s[i]);
            piv++;
        }
    }
    swap(s[piv],s[high]);
    return piv;
}

void quick_sort(int s[],int left,int right)
{
    if (left<right)
    {
        int piv = partition(s,left,right);
        quick_sort(s,left,piv-1);
        quick_sort(s,piv+1,right);
    }
}

4.5 歸并排序

歸并排序是采用分治法的一個(gè)非常典型的應(yīng)用,它要做兩件事情:

  • “分”, 就是將數(shù)組盡可能的分茴肥,一直分到原子級(jí)別坚踩;
  • “并”,將原子級(jí)別的數(shù)兩兩合并排序瓤狐,最后產(chǎn)生結(jié)果瞬铸。

至于二個(gè)有序數(shù)列合并,只要比較二個(gè)數(shù)列的第一個(gè)數(shù)础锐,誰(shuí)小就先取誰(shuí)安放到臨時(shí)隊(duì)列中嗓节,取了后將對(duì)應(yīng)數(shù)列中這個(gè)數(shù)刪除,直到一個(gè)數(shù)列為空皆警,再將另一個(gè)數(shù)列的數(shù)據(jù)依次取出即可拦宣。

void merge(int* array, int tempArray, int left, int middle, int right)
{
    for(int i=left;i<=right;i++)    tempArray[i] = array[i];
    int s1 = left, s2 = mid +1, temp = left;
    while(s1 <=mid && s2 <= right)
    {
        if(tempArray[s1] <= tempArray[s2])
            array[temp++] = tempArray[s1++];
        else    array[temp++] = tempArray[s2++];
    }
    while(s1 <= mid) array[temp++] = tempArray[s1++];
    while(s2 <= right) array[temp] = tempArray[s2++];
}

void merge_sort(int* array, int* tempArray, int left, int right)
{
    if(left >= right)    return ;
    int mid = left + (right - left)/2;
    int tempArray = new int[right-left+1];
    merge_sort(array,tempArray,left,mid);
    merge_sort(array,tempArray,mid+1,right);
    merge(array,tempArray,left,mid,right);
}

5. 查找算法總結(jié)

5.1 順序查找

設(shè)想有一個(gè)1M的數(shù)據(jù),我們?nèi)绾卧诶锩嬲业轿覀兿胍哪莻€(gè)數(shù)據(jù)信姓。此時(shí)數(shù)據(jù)本身沒有特征鸵隧,所以我們需要的那個(gè)數(shù)據(jù)可能出現(xiàn)在數(shù)組的各個(gè)位置,可能在數(shù)據(jù)的開頭位置意推,也可能在數(shù)據(jù)的結(jié)束位置豆瘫。這種性質(zhì)要求我們必須對(duì)數(shù)據(jù)進(jìn)行遍歷之后才能獲取到對(duì)應(yīng)的數(shù)據(jù)。

int find(int array[], int len, int val)
{
    if(array == NULL && length == 0)    return -1;
    for(int i=0;i<len;i++){
        if(val == array[i])
            return i;
    }
    return -1;
}

分析:由于我們不清楚這個(gè)數(shù)據(jù)判斷究竟需要多少次左痢。但是靡羡,我們知道,這樣一個(gè)數(shù)據(jù)查找最少需要1次俊性,那么最多需要n次略步,平均下來可以看成是(1+n)/2,差不多是n的一半定页。我們把這種比較次數(shù)和n成正比的算法復(fù)雜度記為o(n)趟薄。

5.2 二分查找

如果數(shù)據(jù)排列地非常整齊,那結(jié)果會(huì)是什么樣的呢典徊?就像在生活中杭煎,如果平時(shí)不注意收拾整齊恩够,那么找東西的時(shí)候非常麻煩,效率很低羡铲;但是一旦東西放的位置固定下來蜂桶,所有東西都?xì)w類放好,那么結(jié)果就不一樣了也切,我們就會(huì)形成思維定勢(shì)扑媚,這樣查找東西的效率就會(huì)非常高。那么雷恃,對(duì)一個(gè)有序的數(shù)組疆股,我們應(yīng)該怎么查找呢?二分法就是最好的方法倒槐。

int BinaryFind(int array[], int len, int val)
{
    if(array == NULL && length == 0)    return -1;
    int start = 0, end = len-1, middle=0;
    while(start<=end){
        middle = start + (end - start)/2;
        if(val == array[middle])    return middle;
        else if(val > array[middle])    start = middle+1;
        else end = middle-1;
    }
    return -1;
}

分析:上面我們說到普通的數(shù)據(jù)查找算法復(fù)雜度是o(n)旬痹。那么我們可以用上面一樣的方法判斷一下算法復(fù)雜度。這種方法最少是1次讨越,那么最多需要多少次呢两残?我們發(fā)現(xiàn)最多需要log(n+1)/log(2)即可。

5.3 二叉搜索樹

二叉搜索樹是二分查找的二叉樹實(shí)現(xiàn)谎痢,二叉搜索樹每個(gè)結(jié)點(diǎn)都有作為搜索依據(jù)的關(guān)鍵碼磕昼,所有結(jié)點(diǎn)的關(guān)鍵碼互不相同;左子樹(若存在)上的所有結(jié)點(diǎn)的關(guān)鍵碼都小于根結(jié)點(diǎn)的關(guān)鍵碼节猿;右子樹(若存在)上的所有結(jié)點(diǎn)的關(guān)鍵碼都大于根結(jié)點(diǎn)的關(guān)鍵碼票从;左子樹和右子樹也是二叉搜索樹。

如果對(duì)一棵二叉搜索樹進(jìn)行中序遍歷滨嘱,可以按從小到大的順序峰鄙,將各結(jié)點(diǎn)關(guān)鍵碼排列起來,所以也稱為二叉排序樹太雨。

平衡二叉樹是一種高度平衡的二叉搜索樹吟榴,是為了提高二叉搜索樹的效率,減少樹的平均搜索長(zhǎng)度囊扳。其具有以下性質(zhì):其左右子樹都是平衡二叉樹吩翻,且左子樹和右子樹的高度之差的絕對(duì)值不超過1。如過它有n個(gè)結(jié)點(diǎn)锥咸,其高度可保持在O(log2n)狭瞎。

typedef struct _NODE
{
    int data;
    struct _NODE* left;
    struct _NODE* right;
}NODE;

const NODE* find_data(const NODE* pNode, int data){
    if(NULL == pNode)
        return NULL;

    if(data == pNode->data)
        return pNode;
    else if(data < pNode->data)
        return find_data(pNode->left, data);
    else
        return find_data(pNode->right, data);        
}

5.4 Hash表

typedef struct _LINK_NODE
{
    int data;
    struct _LINK_NODE* next;
}LINK_NODE;

LINK_NODE* hash_find(LINK_NODE* array[], int mod, int data)
{
    int index = data % mod;
    if(NULL == array[index])
        return NULL;

    LINK_NODE* pLinkNode = array[index];
    while(pLinkNode){
        if(data == pLinkNode->data)
            return pLinkNode;
        pLinkNode = pLinkNode->next;
    }

    return pLinkNode;
}

分析:hash表因?yàn)椴恍枰判颍贿M(jìn)行簡(jiǎn)單的歸類搏予,在數(shù)據(jù)查找的時(shí)候特別方便熊锭。查找時(shí)間的大小取決于mod的大小。mod越小,那么hash查找就越接近于普通查找碗殷;那么hash越大呢精绎,那么hash一次查找成功的概率就大大增加。

6. 二分查找的變種

6.1 給定一個(gè)有序(非降序)數(shù)組A锌妻,可含有重復(fù)元素代乃,求最小的i使得A[i]等于target,不存在則返回-1

此題也就是求target在數(shù)組中第一次出現(xiàn)的位置从祝。這里可能會(huì)有人想先直接用原始的二分查找襟己,如果不存在直接返回-1,如果存在牍陌,然后再順序找到這個(gè)等于target值區(qū)間的最左位置,這樣的話员咽,最壞情況下的復(fù)雜度就是O(n)了毒涧,沒有完全發(fā)揮出二分查找的優(yōu)勢(shì)。這里的解法具體過程請(qǐng)參考實(shí)現(xiàn)代碼與注釋贝室。

int searchFirstPos(int A[], int n, int target)  
{  
    if(n <= 0) return -1;  
    int low = 0, high = n-1;  
    while(low < high)  
    {  
        int mid = low+((high-low)>>1);  
        if(A[mid] < target)  
            low = mid+1;  
        else // A[mid] >= target  
            high = mid;  
    }  
    /*  
    循環(huán)過程中契讲,當(dāng)low大于0時(shí),A[low-1]是小于target的滑频,因?yàn)锳[mid] < target時(shí)捡偏, 
    low=mid+1;當(dāng)high小于n-1時(shí)峡迷,A[high]是大于等于target的银伟,因?yàn)锳[mid] >= target時(shí), 
    high = mid绘搞;循環(huán)結(jié)束時(shí)彤避,low 等于 high,所以夯辖,如果A[low](A[high])等于target琉预, 
    那么low(high)就是target出現(xiàn)的最小位置,否則target在數(shù)組中不存在蒿褂。 
    */  
    if(A[low] != target)  
        return -1;  
    else  
        return low;  
}  

6.2 給定一個(gè)有序(非降序)數(shù)組A圆米,可含有重復(fù)元素,求最大的i使得A[i]等于target啄栓,不存在則返回-1

int searchLastPos(int A[], int n, int target)  
{     
    if(n <= 0) return -1;  
    int low = 0, high = n-1;  
    while(low < high)  
    {  
        /* 
        這里中間位置的計(jì)算就不能用low+((high-low)>>1)了娄帖,因?yàn)楫?dāng)low+1等于high 
        且A[low] <= target時(shí),會(huì)死循環(huán)谴供;所以這里要使用low+((high-low+1)>>1)块茁, 
        這樣能夠保證循環(huán)會(huì)正常結(jié)束。 
        */  
        int mid = low+((high-low+1)>>1);  
        if(A[mid] > target)  
            high = mid-1;  
        else // A[mid] <= target  
            low = mid;  
    }  
    /*  
    循環(huán)過程中,當(dāng)high小于n-1時(shí)数焊,A[high+1]是大于target的永淌,因?yàn)锳[mid] > target時(shí), 
    high=mid-1佩耳;當(dāng)low大于0時(shí)遂蛀,A[low]是小于等于target的,因?yàn)锳[mid] <= target時(shí)干厚, 
    low = mid李滴;循環(huán)結(jié)束時(shí),low 等于 high蛮瞄,所以所坯,如果A[high](A[low])等于target, 
    那么high(low)就是target出現(xiàn)的最大位置挂捅,否則target在數(shù)組中不存在芹助。 
    */  
    if(A[high] != target)  
        return -1;  
    else  
        return high;  
}

6.3 給定一個(gè)有序(非降序)數(shù)組A,可含有重復(fù)元素闲先,求target在數(shù)組中出現(xiàn)的次數(shù)

求出第一次出現(xiàn)位置和最后一次出現(xiàn)位置状土。由于前面都已實(shí)現(xiàn),這里不多解釋伺糠。

int count(int A[], int n, int target)  
{  
    int firstPos = searchFirstPos(A, n, target); // 第一次出現(xiàn)位置  
    if(firstPos == -1)  
        return 0;  
    int lastPos = searchLastPos(A, n, target);  // 最后一次出現(xiàn)位置  
    return lastPos-firstPos+1;  // 出現(xiàn)次數(shù)  
}

7. 數(shù)學(xué)相關(guān)的算法

7.1 計(jì)算最大公約數(shù)

輾轉(zhuǎn)相除法
例1 .求兩個(gè)正數(shù)8251和6105的最大公因數(shù).
(分析:輾轉(zhuǎn)相除→余數(shù)為零→得到結(jié)果)
8251=6105×1+2146
顯然8251與6105的最大公因數(shù)也必是2146的因數(shù),同樣6105與2146的公因數(shù)也必是8251的因數(shù),所以8251與6105的最大公因數(shù)也是6105與2146的最大公因數(shù).
6105=2146×2+1813
2146=1813×1+333
1813=333×5+148
333=148×2+37
148=37×4+0
則37為8251與6105的最大公因數(shù).

利用輾轉(zhuǎn)相除法求最大公因數(shù)的步驟如下:
第一步:用較大的數(shù)m除以較小的數(shù)n得到一個(gè)商q0和一個(gè)余數(shù)r0蒙谓;
第二步:若r0=0,則n為m,n的最大公因數(shù);若r0≠0,則用除數(shù)n除以余數(shù)r0得到一個(gè)商q1和一個(gè)余數(shù)r1训桶;
第三步:若r1=0,則r1為m,n的最大公因數(shù)累驮;若r1≠0,則用除數(shù)r0除以余數(shù)r1得到一個(gè)商q2和一個(gè)余數(shù)r2;
……
依次計(jì)算直至rn=0,此時(shí)所得到的rn-1即為所求的最大公因數(shù).

//遞歸版本
int gcd(int a,int b) 
{ 
    int g; 
    if(b==0) 
        g=a; 
    else 
        g=gcd(b,a%b); 
    return g; 
}
//迭代版本
int gcd(int a, int b)
{
    int c;
    if(a<b) swap(a,b);
    while(b != 0){
        c = a%b;
        a = b;
        b = c;
    }
    return a;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末渊迁,一起剝皮案震驚了整個(gè)濱河市慰照,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌琉朽,老刑警劉巖毒租,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異箱叁,居然都是意外死亡墅垮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門耕漱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來算色,“玉大人,你說我怎么就攤上這事螟够≡置危” “怎么了峡钓?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)若河。 經(jīng)常有香客問我能岩,道長(zhǎng),這世上最難降的妖魔是什么萧福? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任拉鹃,我火速辦了婚禮,結(jié)果婚禮上鲫忍,老公的妹妹穿的比我還像新娘膏燕。我一直安慰自己,他們只是感情好悟民,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布坝辫。 她就那樣靜靜地躺著,像睡著了一般逾雄。 火紅的嫁衣襯著肌膚如雪阀溶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天鸦泳,我揣著相機(jī)與錄音,去河邊找鬼永品。 笑死做鹰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鼎姐。 我是一名探鬼主播钾麸,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼炕桨!你這毒婦竟也來了饭尝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤献宫,失蹤者是張志新(化名)和其女友劉穎钥平,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姊途,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涉瘾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了捷兰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片立叛。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖贡茅,靈堂內(nèi)的尸體忽然破棺而出秘蛇,到底是詐尸還是另有隱情其做,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布赁还,位于F島的核電站妖泄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏秽浇。R本人自食惡果不足惜浮庐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柬焕。 院中可真熱鬧审残,春花似錦、人聲如沸斑举。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)富玷。三九已至璧坟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赎懦,已是汗流浹背雀鹃。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留励两,地道東北人黎茎。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像当悔,于是被迫代替她去往敵國(guó)和親傅瞻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1 序 2016年6月25日夜盲憎,帝都嗅骄,天下著大雨,拖著行李箱和同學(xué)在校門口照了最后一張合照饼疙,搬離寢室打車去了提前租...
    RichardJieChen閱讀 5,074評(píng)論 0 12
  • 第一章 緒論 什么是數(shù)據(jù)結(jié)構(gòu)溺森? 數(shù)據(jù)結(jié)構(gòu)的定義:數(shù)據(jù)結(jié)構(gòu)是相互之間存在一種或多種特定關(guān)系的數(shù)據(jù)元素的集合。 第二章...
    SeanCheney閱讀 5,742評(píng)論 0 19
  • 樹的概述 樹是一種非常常用的數(shù)據(jù)結(jié)構(gòu)宏多,樹與前面介紹的線性表儿惫,棧,隊(duì)列等線性結(jié)構(gòu)不同伸但,樹是一種非線性結(jié)構(gòu) 1.樹的定...
    Jack921閱讀 4,432評(píng)論 1 31
  • 散文集|寧子的生涯 文/寧子 青青坐在槐花樹下發(fā)呆肾请,嘴里叼著隨手摘下的狗尾巴草,目光散漫地落在前方小溪中月亮的倒影...
    Ningzi閱讀 382評(píng)論 2 2
  • 冬季的衣服鞋子帶回家 書籍能賣的就賣出去吧 閑置電器打包帶回家或賣給有需要的人 少儲(chǔ)備干糧 順帶清理一下垃圾更胖,什么...
    Tweetyzhang閱讀 129評(píng)論 0 0