MOOC数据结构

mooc摘记

第一讲

1.捕捉程序的运行时间

image

2.解决问题的方法与效率

①数据的组织方式
②空间的利用效率
③算法的巧妙程度

第二讲

第九讲 排序上

1.冒泡排序

void Bubble_Sort(long long *x,long long n){//冒泡排序
	long long temp;
	for(int i=n-1;i>0;--i){
		for(int j=0;j<i;++j){
			if(x[j]>x[j+1]){
				temp=x[j];
				x[j]=x[j+1];
				x[j+1]=temp;
			}
		}
	}
	return ;
}

2.插入排序

void Insertion_Sort(long long *x,long long n){//插入排序
	long long temp,j;
	for(int i=1;i<n;++i){
		temp=a[i];
		for(j=i;j>0;--j){
			if(temp<a[j-1]){
				a[j]=a[j-1];
			}else break;
		}
		a[j]=temp;
	}
	return ;
}

插排例题1
对一组包含10个元素的非递减有序序列,采用插入排序排成非递增序列,其可能的比较次数和移动次数分别是:
A. 45,44 B. 54,63 C.100,54 D. 100,100
个人觉得,对于十个元素的非递减序列,插排成非递增序列,那么比较次数最多也就是1+2+3+4+5+6+7+8+9=45次,移动次数也为45次。当出现相同元素时,比较次数会减少。所以A选项比较合理。

定理:任何仅以交换相邻两元素来排序的算法,其平均时间复杂度为\(O(n^2)\)

3.归并排序
这里的函数传参遇到大数值时比较慢,此时推荐全局变量

void Merge(int r[], int r1[], int s, int m, int t){//合并两个升序数组为一个升序数组
	int i = s, j = m + 1, k = s;
	while(i <= m && j <= t){
		if(r[i] <= r[j]) r1[k ++] = r[i ++];
		else r1[k ++] = r[j ++];
	}
	while(i <= m) r1[k ++] = r[i ++];
	while(j <= t) r1[k ++] = r[j ++];
	return ;
}

void Merge_Sort(int r[], int s, int t){//归并排序
	if(s != t){//可以划分
		int m = (s + t) / 2, r1[maxm];
		Merge_Sort(r, s, m);//求解子问题1
		Merge_Sort(r, m + 1, t);//求解子问题2
		Merge(r, r1, s, m, t);//合并两个序列
		for(int i = s; i <= t; ++ i)//结果复制到r数组中
			r[i] = r1[i];
	}
	return ;
}

4.快速排序

int Partition(int r[], int begin, int end){// 划分
	int i = begin, j = end;
	while(i < j){
		while(i < j && r[i] <= r[j]) -- j;// 右侧扫描
		if(i < j){// 将小元素放在前面
			int temp = r[i];
			r[i] = r[j];
			r[j] = temp;
			++ i;
		}
		while(i < j && r[i] <= r[j]) ++ i;// 左侧扫描
		if(i < j){// 将大元素放在后面
			int temp = r[i];
			r[i] = r[j];
			r[j] = temp;
			-- j;
		}
	}
	return i;
}

void Quick_Sort(int r[], int begin, int end){// 快速排序
	int pos;
	if(begin < end){
		pos = Partition(r, begin, end);
		Quick_Sort(r, begin, pos - 1);
		Quick_Sort(r, pos + 1, end);
	}
	return ;
}

PTA练习摘记

01-复杂度1 最大子列和问题

最大子列和问题,mooc一共给出了四种解法。推荐第四种做法。
解法一:
遍历子列的左右端点,循环求和,思路简单,但时间复杂度高
image
解法二:
遍历子列的左端点,循环右端点求和,思路依旧简单,但时间复杂度低了一个档次
image
解法三:
分治法,先考虑左半边的最大子列和,再考虑右半边的最大子列和,最后考虑跨越中间分界线的最大子列和,那么当前想求的最大子列和即为三数中的最大值,之后递归求解整个数组。
image
代码:

int Max3( int A, int B, int C )
{ /* 返回3个整数中的最大值 */
    return A > B ? A > C ? A : C : B > C ? B : C;
}

int DivideAndConquer( int List[], int left, int right )
{ /* 分治法求List[left]到List[right]的最大子列和 */
    int MaxLeftSum, MaxRightSum; /* 存放左右子问题的解 */
    int MaxLeftBorderSum, MaxRightBorderSum; /*存放跨分界线的结果*/

    int LeftBorderSum, RightBorderSum;
    int center, i;

    if( left == right )  { /* 递归的终止条件,子列只有1个数字 */
        if( List[left] > 0 )  return List[left];
        else return 0;
    }

    /* 下面是"分"的过程 */
    center = ( left + right ) / 2; /* 找到中分点 */
    /* 递归求得两边子列的最大和 */
    MaxLeftSum = DivideAndConquer( List, left, center );
    MaxRightSum = DivideAndConquer( List, center+1, right );

    /* 下面求跨分界线的最大子列和 */
    MaxLeftBorderSum = 0; LeftBorderSum = 0;
    for( i=center; i>=left; i-- ) { /* 从中线向左扫描 */
        LeftBorderSum += List[i];
        if( LeftBorderSum > MaxLeftBorderSum )
            MaxLeftBorderSum = LeftBorderSum;
    } /* 左边扫描结束 */

    MaxRightBorderSum = 0; RightBorderSum = 0;
    for( i=center+1; i<=right; i++ ) { /* 从中线向右扫描 */
        RightBorderSum += List[i];
        if( RightBorderSum > MaxRightBorderSum )
            MaxRightBorderSum = RightBorderSum;
    } /* 右边扫描结束 */

    /* 下面返回"治"的结果 */
    return Max3( MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum );
}

int MaxSubseqSum3( int List[], int N )
{ /* 保持与前2种算法相同的函数接口 */
    return DivideAndConquer( List, 0, N-1 );
}
ll n, a[maxm];

ll calc(int l, int r){
	if(l == r) return a[l];
	int m = l + r >> 1;
	ll max1 = calc(l, m), max2 = calc(m + 1, r), max3;
	ll maxl = 0, maxr = 0, t;
	t = 0;
	for(int i = m; i >= l; -- i){
		t += a[i];
		maxl = max(maxl, t);
	}
	t = 0;
	for(int i = m + 1; i <= r; ++ i){
		t += a[i];
		maxr = max(maxr, t);
	}
	max3 = maxl + maxr;
	return max(max1, max(max2, max3));
}

void solve(){
	cin >> n;
	for(int i = 1; i <= n; ++ i) cin >> a[i];
	cout << calc(1, n);
	return ;
}

第四种:在线处理
怎么说,就是遍历数组,累计和并统计最大和,当累计和小于0时,其不能对后面的子列最大和起到正向作用,所以直接将累计和置0。
image

期中考试摘记

单选题

1.已知一棵完全二叉树的第6层(设根为第1层)有8个叶结点,则该完全二叉树的结点个数最多是:
A.119 B.111 C.39 D.52

解:首先回顾完全二叉树的特点——叶结点仅出现在最后的两层。
当第六层有8个叶结点时
第一种情况:树的深度为6,那么树的结点数为\(2^5-1+8=39\);
第二种情况:树的深度为7,那么树的结点数为\(2^6-1+(2^5-8)*2=111\)
故最大的结点个数为111

期末考试摘记

前记:做题的时候忘记滑动了,提交了之后才发现最后一个编程题没有写,呜呜呜

判断题

  1. \(NlogN^2与NlogN\)具有相同的增长速度。

应当是对的?\(NlogN^2=2NlogN\)

选择题

  1. 在拓扑排序算法中用堆栈和用队列产生的结果会不同吗?
    A.以上全不对 B.有可能会不同 C.肯定是相同的 D.是的肯定不同

B.有可能不同,使用堆栈最后得到的就是从栈顶到栈底就是逆拓扑的有序序列,而使用队列就是正拓扑的有序序列。
考虑极端情况链式图,两者的结果就相同

  1. 若数据元素序列{ 11,12,13,7,8,9,23,4,5 }是采用下列排序方法之一得到的第二趟排序后的结果,则该排序算法只能是:
    A.插入排序 B.归并排序 C.选择排序 D.冒泡排序

再复习一下归并排序哈!这里我们其实可以清楚的知道,二躺排序后,选择排序和冒泡排序可以实现至少最大或者最小2个元素有序,而题目所给的数据不支持这个结论,故只能是插入排序,实现前2个元素有序

  1. 给定一有向图的邻接表如下。从顶点V1出发按深度优先搜索法进行遍历,则得到的一种顶点序列为:
    image
    A.V1,V2,V4,V5,V3
    B.V1,V2,V3,V5,V4
    C.V1,V3,V4,V5,V2
    D.V1,V4,V3,V5,V2

喂喂喂,深度优先!仔细点想想什么是深度优先搜索算法。可能的序列应该是C

编程题

呜呜呜,没做上
下面是原题:
R7-1 根据后序和中序遍历输出先序遍历
本题要求根据给定的一棵二叉树的后序遍历和中序遍历结果,输出该树的先序遍历结果。
输入格式:
第一行给出正整数\(N(≤30)\),是树中结点的个数。随后两行,每行给出\(N\)个整数,分别对应后序遍历和中序遍历结果,数字间以空格分隔。题目保证输入正确对应一棵二叉树。
输出格式:
在一行中输出$Preorder: $以及该树的先序遍历结果。数字间有1个空格,行末不得有多余空格。
输入样例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
输出样例:
Preorder: 4 1 3 2 6 5 7
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB

posted on 2023-03-10 22:43  Qiansui  阅读(45)  评论(0编辑  收藏  举报