数据结构1

最近在学习清华邓俊辉老师的数据结构与算法,简单整理下。

 Computer science should be called computing science,for the same reason why surgery is not called knife science.

一、 绪论 

(随性而记的笔记)

1. 算法(信息处理)

算法是借助某种工具,遵照一定的规则,以明确而机械的形式进行

计算模型 = 计算机 = 信息处理工具

所谓算法,即特定计算模型下,旨在解决特定问题的指令序列。

输入(待处理的信息)、输出(经处理的信息)、正确性(可以解决指定的问题)、确定性(任一算法都可以描述为一个由基本操作组成的序列)、可行性(每一基本操作都可以实现,且在常数时间内完成)、有穷性(对于任何输入,经有穷次基本操作,都可以得到输出)

Hailstone序列的例子

#include <stdio.h>
#include <stdlib.h>

int hailstone(int n);

int main()
{
    int n = 0;
    int len = 0;

    scanf("%d",&n);
    len = hailstone(n);

    printf("hailstone的长度是:%d",len);
    return 0;
}

int hailstone(int n)
{
    int length = 1;
    while(n > 1){
        (n%2) ? n = (3*n + 1) : (n = n/2);
        length++;
    }
    return length;
}

一开始我编写这个程序时,在  (n%2) ? n = (3*n + 1) : (n = n/2); 这一语句,在问号的后面的表达式我没有添加括号(我实在codeblocks上运行该程序),然后会出现error: lvalue required as left operand of assignment(我想这应该是算数符号的优先级导致的)所以就改成上述的语句。所以这个提醒着我们加括号真的很重要啊!!!

冒泡排序

话不多说,直接上代码吧

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int A[] = {2, 3, 7, 6, 8, 1, 9};
    int len = 0;
    int i = 0;
    int j = 0;
    int temp = 0;

    len = sizeof(A)/sizeof(int);

    /*while(len-- > 0){
        for(i = 0, j = 1;j < len; i++,j++){
            if(A[i] > A[j]){
                temp = A[i];
                A[i] = A[j];
                A[j] = temp;
            }
        }
    }*/
    for(i = 0; i < len; i++){
        for(j = 0; j < len - 1 - i; j++){
            if(A[j] > A[j+1]){
                temp = A[j];
                A[j] = A[j+1];
                A[j+1] = temp;
            }
        }
    }

    for(i = 0; i < len; i++){
        printf("%d\n",A[i]);
    }
    return 0;
}

一开始采用的是while循环,但是到最后打印数组时,我发现竟然没法打印数组,但是程序运行正常,没有报错。所以我开始打断点调试,一步一步的执行,最后发现程序中len在最后会变成0,随意没法打印数组。最后使用了for循环。(编程序还是要细心啊)当然这个程序也可以使用递归方法解决,利用减而治之的思想。

减而治之分而治之

减而治之和分而治之是递归中两个很重要的思想。(接来下上图吧,盗用老师上课的PPT,私自截的图)

 

接下来当然是举例子了呀。

数组倒置,任给数组A[0,n),将其前后颠倒。

这个例子大家肯定很熟悉,一般我们采用的方法就是迭代

next:
   if(lo < hi){
     swap((A[lo],A[hi]));
     lo++;
     hi--;
     goto next;
   }
或是
while(lo < hi){
   swap((A[lo],A[hi]));  /*或是更精简的swap((A[lo++],A[hi--]));*/
   lo++;
   hi--;
}

我们也可以采用迭代方法来解决这个问题,采用减而治之的思想。

统一接口:

void reverse(int *A, int lo, int hi);

递归版:

if(lo < hi){
   swap(A[lo], A[hi]);
   reverse(A, lo + 1, hi - 1);
}

递归的思想就是先颠倒 A[lo] 和 A[hi],然后原数组中将这两个值从数组中剔除,形成了一个行的数组,然后采用同样的方法继续颠倒这个新数组的 A[lo] 和 A[hi],一直进行下去,直到 lo > hi ,结束递归。。。

在数组A[0,n]中找到最大的两个数值。

首先我想到的就是,冒泡排序法来寻找,因为是需要找到最大的两个。所以我的外循环只需两次,将最大的两个数放在数组的最后两位,代码如下:

int main()
{
    int A[] = {2, 3, 7, 6, 8, 1, 9};
    int len = 0;
    int i = 0;
    int j = 0;
    int temp = 0;

    len = sizeof(A)/sizeof(int);
    for(i = 0; i < 2; i++){
        for(j = 0; j < len - 1 - i; j++){
            if(A[j] > A[j+1]){
                temp = A[j];
                A[j] = A[j+1];
                A[j+1] = temp;
            }
        }
    }

    printf("%d\n%d\n",A[len-1],A[len-2]);

    return 0;
}

这样就可以简单的实现寻找数组中最大的两个数。

接下来使用的是另一种迭代的方法(采用的C++方法)

void max2(int A[], int lo, int hi, int &x1, int &x2);
void swap(int *a, int *b);

int main()
{
    int A[] = {2, 3, 7, 6, 8, 1, 9};
    int lo = 0;
    int hi = 0;
    int lo1 = 0;
    lo1 = lo + 1;
    int & x1 = lo;
    int & x2 = lo1;

    hi = sizeof(A)/sizeof(int);
    max2(A, lo, hi, x1, x2);

    cout << A[x1] << endl;
    cout << A[x2] << endl;
    return 0;
}

void max2(int A[], int lo, int hi, int &x1, int &x2)
{
    int i = 0;

    if(A[x1] > A[x2]){
        swap(x1,x2);
    }
    for(i = lo + 2; i < hi; i++){
        if(A[x2] < A[i]){
            if(A[x1] < A[x2 = i]){
                swap(x1,x2);
            }
        }
    }
}

void swap(int &a, int &b)
{
    int temp = 0;
    temp = a;
    a = b;
    b = temp;
}

假如在数组中最大的两个值刚好出现在数组的靠前部分,这样在迭代过程中可以减少很多计算,for循环中的第二个if语句就可以不用执行,相比较上一种迭代的方法,计算量就会少很多。

C++引用的用法见:https://blog.csdn.net/bzhxuexi/article/details/17588803

接下来就是“递归+分治”的思想,还是直接上代码吧

#include <iostream>

using namespace std;

void max2(int A[], int lo, int hi, int &x1, int &x2);
void swap(int *a, int *b);

int main()
{
    int A[] = {45, 99, 2, 1, 2, 3, 7, 6, 8, 11, 8, 1, 9, 10};
    /*int A[] = {2, 3, 7};*/
    int lo = 0;
    int hi = 0;
    /*int lo1 = 0;
    lo1 = lo + 1;*/
    int x1 = 0;
    int x2 = 0;

    hi = sizeof(A)/sizeof(A[0]) - 1;
    max2(A, lo, hi, x1, x2);

    cout << A[x1] << endl;
    cout << A[x2] << endl;
    cout << hi << endl;

    return 0;
}

void max2(int A[], int lo, int hi, int &x1, int &x2)
{

    if(lo + 1 == hi){
        if(A[x1 = lo] < A[x2 = hi]){
            swap(x1,x2);
            return;
        }
        return;
    }
    if(lo + 2 == hi){
        if(A[x1 = lo] > A[x2 = lo + 1] ){
            if(A[x1] > A[hi]){
                if(A[x2] > A[hi]){
                    return;
                }
                else{
                    x2 = hi;
                    return;
                }
            }
            else{
                swap(x1,x2);
                x1 = hi;
                return;
            }
        }
        else{
            swap(x1,x2);
            if(A[x1] > A[hi]){
                if(A[x2] > A[hi]){
                    return;
                }
                else{
                    x2 = hi;
                    return;
                }
            }
            else{
                swap(x1,x2);
                x1 = hi;
                return;
            }
        }
    }

    int x1L;
    int x2L;
    int x1R;
    int x2R;
    int mi = 0;

    mi = (lo + hi)/2;

    max2(A, lo, mi, x1L, x2L);
    max2(A, mi+1, hi, x1R, x2R);

    if(A[x1L] > A[x1R]){
        x1 = x1L;
        x2 = (A[x2L] > A[x1R]) ? x2L : x1R;
    }else{
        x1 = x1R;
        x2 = (A[x2R] > A[x1L]) ? x2R : x1L;
    }

}

void swap(int *a, int *b)
{
    int temp = 0;
    temp = *a;
    *a = *b;
    *b = temp;
}

分而治之的思想就是将数组分成左右序列,分别求出左右序列中最大的两个数,假设左序列中的两个最大值是x1L和x2L(x1L >= x2L),有序列中的两个最大值是x1R和x2R(x1R >= x2R)。那么这个序列中的最大的两个值肯定就在这四个数值中。假如x1L大于x1R,那么最大值就是x1L,第二大数值肯定取(x2L和x1R中的最大值);假如x1R大于x1L,那么最大值就是x1R,第二大数值肯定取(x1L和x2R中的最大值)。划分的子序列可以继续划分下去,直至不能划分为止。说个题外话(本人是个菜鸡,真的很菜)开始编写代码时,代码出现了bug,起调试很久才发现是递归出口的地方出了错,但是我当时找了很久并没与发现是哪里出了错,最后我就想是瞎猫碰见死耗子一样发现是因为我在 (lo + 1 == hi) 这个条件时竟然忘记了返回另一种结果,就是忘记返回第二个 if 语句后面跟这个 else 情况,我记得上课时的时候老师强调过不要忘记 else 的那一部分。

接下来看另一个递归分-而治之思想的例子,归并排序

接下来直接上代码吧

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

void mergeSort(int* A, int low, int high);
void mergeArray(int* A, int low, int mid, int high);

int main()
{
    int A[] = {6, 1, 2, 9, 7, 3};
    int N = 0;
    int i = 0;
    N = sizeof(A)/sizeof(int) - 1;
    mergeSort(A, 0, N);
    for(i = 0; i<=N; i++){
        printf("%d\n",A[i]);
    }
    return 0;
}

void mergeSort(int *A, int low, int high)
{
 if(low >= high){
    return;
 }
 int mid = 0;
 mid = (low + high)/2;
 mergeSort(A, low, mid);
 mergeSort(A, mid + 1, high);
 mergeArray(A, low, mid, high);
}

void mergeArray(int* A, int low, int mid, int high)
{
    int i = 0;
    int j = 0;
    int k = 0;
    int *p = (int*)malloc((high - low + 2)*sizeof(int));
    i = low;
    j = mid + 1;

    while(i <= mid && j <= high){
        if(A[i] < A[j]){
            *(p + k) = A[i++];
            k++;
        }
        else{
            *(p + k) = A[j++];
            k++;
        }
    }
    while(i <= mid){
        *(p + k) = A[i++];
        k++;
    }
    while(j <= high){
        *(p + k) = A[j++];
        k++;
    }
    for(i = low, k = 0; i <= high; i++,k++){
        A[i] = *(p + k);
    }
      free(p);
}

其实以前一直不是很理解关于递归的结束出口在哪里,该如何结束递归,借来上一张我自己理解的图吧,加深理解。以归并排序为例。

怎么图片上传不了,那就下次再上传吧。。。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值