一、《计算思维导论(C 语言)》
1、程序的基本逻辑
①、变量声明与定义
- 变量的赋值分为两种方式:
- 先声明再赋值
int num; num = 10;
- 声明的同时赋值
int num = 10;
- c语言中,数据类型可分为:
最常用的 整型、浮点型、字符型。
-
格式化输出语句:
格式为:
printf("输出格式符",输出项);
-
运算符号:
除法运算中,需注意:
若相除的两个数都是整数,则结果也为整数,小数部分省略
。如 8/3=2;
若两个数中有一个为小数,结果则为小数。如 9.0/2=4.500000
取余运算中,需注意:
若运算只是和用两个整数进行取余运算,如 10%3=1;
但 10.0%3则是错误的。运算后的符号取决于被模数的符号。
如 (-10)%3=-1; 而 10%(-3)=1;
自增与自减运算符:
逻辑运算符:
② 条件语句:
-
if-else
条件语句if(表达式){ 执行代码块1; }else { 执行代码块2; } // 如果表达式为真,则执行代码块1,否则执行代码块2。
-
多重 if-else
条件语句if(表达式){ 执行代码块 1; } ………… else if(表达式 m){ 执行代码块 m; }else{ 执行代码块 n; } // 依次判断表达式的值,当出现某个值为真时,则执行对应代码块,否则执行代码块n。 // 注意:当某一条件为真的时候,则不会向下执行该分支结构的其他语句。
-
嵌套if-else
条件语句会读下面这段代码就可。
#include <stdio.h> int main () { /* 局部变量定义 */ int a = 100; int b = 200; /* 检查布尔条件 */ if( a == 100 ) { /* 如果条件为真,则检查下面的条件 */ if( b == 200 ) { /* 如果条件为真,则输出下面的语句 */ printf("a 的值是 100,且 b 的值是 200\n" ); } } printf("a 的准确值是 %d\n", a ); printf("b 的准确值是 %d\n", b ); return 0; } // a 的值是 100,且 b 的值是 200 // a 的准确值是 100 // b 的准确值是 200
③ 循环语句
-
while
循环while(表达式){ 执行代码块 } // 其中表达式为 循环条件,执行代码块为 循环体 // while 的语义是: 计算表达式的值,当值为真(非0)时, 执行循环体代码块。
-
do-while
循环do{ 执行代码块 }while(表达式); // 注意;这里有分号 // do-while循环语句的语义是: 它先执行循环中的执行代码块,然后再判断while中表达式是否为真,如果为真则继续循环;如果为假,则终止循环。 // 因此,do-while循环至少要执行一次循环语句。重要!!
-
for
循环for(表达式1;表达式2;表达式3){ 执行代码块 } // 执行过程如下: // 执行表达式1,对循环变量进行初始化; // 判断表达式2,若其值为真(非0),则执行for循环体中执行代码块,然后向下执行;若其值为假(0),则结束循环; // 执行表达式3,(i++)等对于循环变量进行操作的语句; // 执行for循环中执行代码块后执行第二步;第一步初始化只会执行一次。 // 循环结束,程序继续向下执行。
-
表达式1是一个或多个赋值语句,它用来控制变量的初始值;
-
表达式2是一个关系表达式,它决定什么时候退出循环;
-
表达式3是循环变量的步进值,定义控制循环变量每循环一次后按什么方式变化。
-
for循环中的“表达式1、2、3”均可不写为空,但两个分号
(;;)
不能缺省。 -
表达式1和表达式3可以是一个简单表达式也可以是多个表达式以逗号分割。
int sum,num; for(sum = 0,num = 0;num <= 3; num++ ,sum++){ sum += num; printf("num=%d,sum=%d\n",num,sum); }
-
④ break 和 continue 语句
-
break
语句在没有循环结构的情况下,break不能单独在if-else语句中。
在多层循环中,一个break只能跳出当前循环。
-
continue
语句continue语句的作用是结束本次循环开始执行下一次循环。
例题:
-
执行以下程序段后,输出结果和a的值是()
int a=10; printf("%d",a++);
A、11 和 10
B、11 和 11
C、10 和 11
D、10 和 10
答案:c
-
能正确表示逻辑关系:" a≥10或a≤0 "的C语言表达式是()
A、a>=0 | a<=10
B、a>=10 or a<=0
C、a>=10 && a<=0
D、a>=10 || a<=0
答案:D
-
该程序的输出结果是( )
int main() { int x=1,a=0,b=0; switch (x) { case 0: b++; case 1: a++; case 2: a++; b++; } printf("a=%d,b=%d",a,b); }
A、2,2
B、2,1
C、1,1
D、1,0
答案:B
switch结构分支没加break;跳出语句,开始执行后,程序由x=1调到case 1 分支 a++(a由0自己加为1),由于没有break语句,程序继续执行到内case 2 分支 a++ b++(a由1自加变2,b由0自加为1),之后程序printf输出 a=2 ,b=1
-
以下程序中,while循环的循环次数是______
main() { int i=0; while(i<10) { if(i<1) continue; if(i==5) break; i++; } }
A、死循环,不能确定次数
B、6
C、4
D、1
A
-
执行以下代码块,若从键盘输入字符abc,并按回车键,则运行结果是( )
#include <stdio.h> int main() { char a; scanf("%c",&a); printf("%c",a); return 0; }
- A. ab
- B. a
- C. b
- D. 空白
答案:B
字符类型只能保存
2、数组
#### ① 数组的初始化与定义
-
数组的声明
数据类型 数组名称[长度]
// 声明一个一维数组 type arrayname [arraySize]; double bookNum [10]; // 现在 bookNum 是一个数组,可以容纳10个类型为double的数字。 // 声明一个二维数组 type arrayName [ x ][ y ]; int x[3][4]; // 可以想象成生成了一个三行四列的表格。
-
数组的初始化
- 一维数组的初始化
// 1、数据类型 数组名称[长度n] = {元素1,元素2 ..... ,元素n}; double bookPrice[5] = {100.0,25.2,522.2,452.9,50.2}; // 2、数据类型 数组名称[] = {元素1,元素2…元素n}; 若省略掉了数组声明式在[]中指定的元素数目,则数组大小为初始化时元素的个数。 double bookPrice[] = {100.0,25.2,522.2,452.9,50.2}; //则这个数组的长度就为5 // 3、数据类型 数组名称[长度n]; // 数组名称[0] = 元素1; // 数组名称[1] = 元素2; // 数组名称[n-1] = 元素n; double bookPrice[5]; bookPrice[0] = 100.0; bookPrice[1] = 25.2; bookPrice[2] = 522.2; bookPrice[3] = 452.9; bookPrice[4] = 50.2;
- 二维数组的初始化
// 多维数组可以通过在括号内每行指定值来进行初始化。下面是一个带有3行4列的数组。 // 内嵌 int a[3][4] ={ {0, 1, 2, 3} , /* 初始化索引号为 0 的行 */ {4, 5, 6, 7} , /* 初始化索引号为 1 的行 */ {8, 9, 10, 11} /* 初始化索引号为 2 的行 */ }; // 或者 直接初始化 int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
-
访问数据元素
// 数组元素可以通过数组名称加索引进行访问。元素的索引是放在方括号内,跟在数组名称的后面。 double FiretBookPrice = bookPrice[0]; // 一维数组 int val = a[2][3]; //二维数组
实例代码阅读:
- 一维数组:
#include <stdio.h> int main () { int n[ 10 ]; /* n 是一个包含 10 个整数的数组 */ int i,j; /* 初始化数组元素 */ for ( i = 0; i < 10; i++ ) { n[ i ] = i + 100; /* 设置元素 i 为 i + 100 */ } /* 输出数组中每个元素的值 */ for (j = 0; j < 10; j++ ) { printf("Element[%d] = %d\n", j, n[j] ); } return 0; } //输出结果: //Element[0] = 100 //Element[1] = 101 //Element[2] = 102 //Element[3] = 103 //Element[4] = 104 //Element[5] = 105 //Element[6] = 106 //Element[7] = 107 //Element[8] = 108 //Element[9] = 109
- 二维数组
#include <stdio.h> int main () { /* 一个带有 5 行 2 列的数组 */ int a[5][2] = { {0,0}, {1,2}, {2,4}, {3,6},{4,8}}; int i, j; /* 输出数组中每个元素的值 */ for ( i = 0; i < 5; i++ ) { for ( j = 0; j < 2; j++ ) { printf("a[%d][%d] = %d\n", i,j, a[i][j] ); } } return 0; } //a[0][0] = 0 //a[0][1] = 0 //a[1][0] = 1 //a[1][1] = 2 //a[2][0] = 2 //a[2][1] = 4 //a[3][0] = 3 //a[3][1] = 6 //a[4][0] = 4 //a[4][1] = 8
② 数组的遍历
对于数组元素的访问和修改是通过数组下标的方式来解决的,数组遍历的原理也是一样,通过 while
循环或者 for
循环直接遍历数组下标从而达到访问或者修改数组值的目的;
访问数组元素时,下标的取值范围为 0 ≤ index < length
;
阅读代码:
// 1、
#include<stdio.h>
int main()
{
int i,j;
for(i=0;i<=30;i++)
{
j=30-i;
if(i*2+j*4==90)
break;
}
printf("i:%d,j:%d",i,j);
return 0;
}
// 输出结果:
// i:15,j:15
// 本题 i+j=30,求 2i+4j=90时,i,j的取值。
// 2、
#include <stdio.h>
int main()
{
int i,k,j;
for(i=3;i<=100;i++)
{
j=1;
for(k=2;k<i;k++)
if(i%k==0)
{
j=0;
break;
}
if(j==1)
{
printf("%d是素数\n",i);
}
}
return 0;
}
// 输出3-100的所有素数 素数就是只能被1和本身整除的数。
// 3、
#include<stdio.h>
int main()
{
int max=0, min = 0;
int x = 5;
scanf("%d",&max);
min = max;
while(x--)
{
scanf("%d",&max);
if(max <= min)
{
min=max;
}
}
printf("%d",min);
return 0;
}
// 利用循环从键盘输入输入6个数,并找出其中的最小值。
3、函数
① 函数的定义与声明
- 函数的定义:
//一般形式:
// 返回类型 函数名称 (参数){
// 函数主体
// }
/* max函数返回两个参数num1,num2中较大的那个数 */
int max(int num1, int num2)
{
/* 局部变量声明 */
int result;
if (num1 > num2) {
result = num1;
} else {
result = num2;
}
return result;
}
- 函数的声明:
函数声明会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。
// 函数的声明格式 : 返回类型 函数名称 (参数);
int max (int num1,int num2);
// 注意:在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明:
int max(int, int);
当在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必需的。在这种情况下,应该在调用函数的文件顶部声明函数。
② 函数的调用
/*
* 1、该函数调用方法为 传值调用 。
* 把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。
*/
#include <stdio.h>
/* 函数声明,给下面的方法进行调用 */
int max(int num1, int num2); /* 函数返回两个数中较大的那个数 */
int swap(int x, int y); /* 函数返回两个数中较大的那个数 */
int main ()
{
/* 局部变量定义 */
int a = 100;
int b = 200;
int ret;
printf("交换前,a 的值: %d\n", a );
printf("交换前,b 的值: %d\n", b );
/* 调用函数来获取最大值 */
ret = max(a, b);
/* 调用函数来交换值 */
swap(a, b);
printf( "Max value is : %d\n", ret );
printf("交换后,a 的值: %d\n", a );
printf("交换后,b 的值: %d\n", b );
return 0;
}
/* 函数返回两个数中较大的那个数 */
int max(int num1, int num2)
{
/* 局部变量声明 */
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
/* 交换x和y的值 */
void swap(int x, int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
return;
}
// 输出结果:
// 交换前,a 的值: 100
// 交换前,b 的值: 200
// Max value is : 200
// 交换后,a 的值: 100
// 交换后,b 的值: 200
/*
* 2、该函数调用方法为 引用方式 。
*通过引用传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。传递指针可以让 *多个函数访问指针所引用的对象,而不用把对象声明为全局可访问。
*/
#include <stdio.h>
/* 函数声明 */
void swap(int *x, int *y);
int main ()
{
/* 局部变量定义 */
int a = 100;
int b = 200;
printf("交换前,a 的值: %d\n", a );
printf("交换前,b 的值: %d\n", b );
/* 调用函数来交换值
* &a 表示指向 a 的指针,即变量 a 的地址
* &b 表示指向 b 的指针,即变量 b 的地址
*/
swap(&a, &b);
printf("交换后,a 的值: %d\n", a );
printf("交换后,b 的值: %d\n", b );
return 0;
}
/* 函数定义 */
void swap(int *x, int *y)
{
int temp;
temp = *x; /* 保存地址 x 的值 */
*x = *y; /* 把 y 赋值给 x */
*y = temp; /* 把 temp 赋值给 y */
return;
}
// 交换前,a 的值: 100
// 交换前,b 的值: 200
// 交换后,a 的值: 200
// 交换后,b 的值: 100
4、指针
首先我们要知道,每一个变量都有一个内存位置,每一个内存位置都可使用 &
运算符访问的地址,它表示了在内存中的一个地址。
① 指针的定义
指针也就是内存地址
,指针变量是用来存放内存地址的变量
。
// 指针变量声明的一般形式为 :指针的基本类型 *指针变量的名称
int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */
// 声明指针并直接初始化
int *p = 10; //声明一个int类型的指针指向变量为10的内存地址
double *p = 10.55; //声明一个double类型的指针指向变量为10.55的内存地址
float *p = 10.0; //声明一个float类型的指针指向变量为10.0的内存地址
char *p = "123456"; //声明一个char类型的指针指向变量为"123456"的内存地址
② 指针的使用
#include <stdio.h>
int main ()
{
int var = 20; /* 实际变量的声明 */
int *ip; /* 指针变量的声明 */
ip = &var; /* 在指针变量中存储 var 的地址 */
printf("var 变量的地址: %p\n", &var ); // & 取地址
/* 在指针变量中存储的地址 */
printf("ip 变量存储的地址: %p\n", ip );
/* 使用指针访问值 */
printf("*ip 变量的值: %d\n", *ip );
return 0;
}
// 输出:
// var 变量的地址: 0x7ffeeef168d8
// ip 变量存储的地址: 0x7ffeeef168d8
// *ip 变量的值: 20
大纲要求:使用指针实现交换功能的代码阅读:“
#include <stdio.h>
/* 函数声明 */
void swap(int *x, int *y);
int main ()
{
/* 局部变量定义 */
int a = 100;
int b = 200;
printf("交换前,a 的值: %d\n", a );
printf("交换前,b 的值: %d\n", b );
/* 调用函数来交换值
* &a 表示指向 a 的指针,即变量 a 的地址
* &b 表示指向 b 的指针,即变量 b 的地址
*/
swap(&a, &b);
printf("交换后,a 的值: %d\n", a );
printf("交换后,b 的值: %d\n", b );
return 0;
}
/* 函数定义 */
void swap(int *x, int *y)
{
int temp;
temp = *x; /* 保存地址 x 的值 */
*x = *y; /* 把 y 赋值给 x */
*y = temp; /* 把 temp 赋值给 y */
return;
}
// 交换前,a 的值: 100
// 交换前,b 的值: 200
// 交换后,a 的值: 200
// 交换后,b 的值: 100
简单来说,带*的就是直接拿该地址的值。不带星的就是拿地址,带&那就更是拿地址了。
5、结构体
① 结构体的定义
// 结构体定义由关键字 struct 和结构体名组成,结构体名可以根据需要自行定义
// struct 结构体标签 {
// 标准的变量定义1;
// 标准的变量定义2;
// 标准的变量定义3;
// ...
// } 结构变量 ;
// 在一般情况下,结构体标签、标准的变量定义、结构变量 这 3 部分至少要出现 2 个。
//最全的结构体声明
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book;
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//同时又声明了结构体变量s1
//这个结构体并没有标明其标签
struct
{
int a;
char b;
double c;
} s1;
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//结构体的标签被命名为SIMPLE,没有声明变量
struct SIMPLE
{
int a;
char b;
double c;
};
//用SIMPLE标签的结构体,另外声明了变量t1、t2、t3
struct SIMPLE t1, t2[20], *t3;
//也可以用typedef创建新类型
typedef struct
{
int a;
char b;
double c;
} Simple2;
//现在可以用Simple2作为类型声明新的结构体变量
Simple2 u1, u2[20], *u3;
② 结构体变量的初始化
#include <stdio.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book = {"C 语言", "yyt", "编程语言", 123456};
int main()
{
printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
}
// title : C 语言
// author: RUNOOB
///subject: 编程语言
// book_id: 123456
③ 结构体的使用
#include <stdio.h>
#include <string.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main( )
{
struct Books Book1; /* 声明 Book1,类型为 Books */
struct Books Book2; /* 声明 Book2,类型为 Books */
/* Book1 详述 */
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* Book2 详述 */
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Zara Ali");
strcpy( Book2.subject, "Telecom Billing Tutorial");
Book2.book_id = 6495700;
/* 输出 Book1 信息 */
printf( "Book 1 title : %s\n", Book1.title);
printf( "Book 1 author : %s\n", Book1.author);
printf( "Book 1 subject : %s\n", Book1.subject);
printf( "Book 1 book_id : %d\n", Book1.book_id);
/* 输出 Book2 信息 */
printf( "Book 2 title : %s\n", Book2.title);
printf( "Book 2 author : %s\n", Book2.author);
printf( "Book 2 subject : %s\n", Book2.subject);
printf( "Book 2 book_id : %d\n", Book2.book_id);
return 0;
}
//Book 1 title : C Programming
//Book 1 author : Nuha Ali
//Book 1 subject : C Programming Tutorial
//Book 1 book_id : 6495407
//Book 2 title : Telecom Billing
//Book 2 author : Zara Ali
//Book 2 subject : Telecom Billing Tutorial
//Book 2 book_id : 6495700
二、《数据结构与算法》
1、基本数据结构
- **栈(Stack):**栈是一种特殊的线性表,它只能在一个表的一个固定端进行数据结点的插入和删除操作。
- **队列(Queue):**队列和栈类似,也是一种特殊的线性表。和栈不同的是,队列只允许在表的一端进行插入操作,而在另一端进行删除操作。
- **数组(Array):**数组是一种聚合数据类型,它是将具有相同类型的若干变量有序地组织在一起的集合。
- **链表(Linked List):**链表是一种数据元素按照链式存储结构进行存储的数据结构,这种存储结构具有在物理上存在非连续的特点。
- **树(Tree):**树是典型的非线性结构,它是包括,2 个结点的有穷集合 K。
- **图(Graph):**图是另一种非线性数据结构。在图结构中,数据结点一般称为顶点,而边是顶点的有序偶对。
- **堆(Heap):**堆是一种特殊的树形数据结构,一般讨论的堆都是二叉堆。
- **散列表(Hash table):**散列表源自于散列函数(Hash function),其思想是如果在结构中存在关键字和T相等的记录,那么必定在F(T)的存储位置可以找到该记录,这样就可以不用进行比较操作而直接取得所查记录。
① 选择合适的数据结构
-
当要求存储的线性表长度变化不大,易于实现确定其大小时,为了节约存储空间,宜采用顺序表;反之,当线性表长度变化大,难以估计其存储规模时,采用动态链表作为存储结构为好。
-
如果线性表的操作主要是进行查找,很少做插入和删除操作时,采用顺序表作为存储结构为宜;反之,如果需要对线性表进行频繁的插入或删除等操作时,宜采用链表作为存储结构。并且,若链表的插入和删除主要发生在表的首尾两端,则采用尾指针表示的单循环链表为宜。
如果最常用的操作是取第i个结点及其前驱,则采用(D)存储方式最节省时间。 A 单链表 B 双链表 C 单循环链表 D 顺序表 若某线性表最常用的操作是存取任一指定序号的元素和在最后进行插入和删除运算,则利用(A)存储方式最节省时间。 A 顺序表 B 双链表 C 带头结点的双循环链表 D 单循环链表 对于只在表的首、尾两端进行插入操作的线性表,宜采用的存储结构为( C) A 顺序表 B 用头指针表示的单循环链表 C 用尾指针表示的单循环链表 D 单链表
② 基本数据结构特点的描述
-
数组
构建非常简单,能在 O(1) 的时间里根据数组的下标(index)查询某个元素 -
链表
灵活地分配内存空间 -
栈
后进先出(LIFO) -
队列
先进先出(FIFO) -
二叉树
递归
③ 二叉树结点遍历顺序
对二叉树的遍历,就是把树中的结点变成某种意义的线性序列。
遍历算法就是递归。
- 前序遍历,若二叉树为null,则空操作返回,否则先访问根结点,然后前序遍历左子树,在前序遍历右子树。
- 中序遍历,若树为null,则空操作返回,否则从根结点开始,中序遍历左子树,然后访问根结点,最后中序遍历右子树。
- 后序遍历,若树为null,则空操作返回,否则从左到右,先叶子后结点的方式,先遍历访问左子树,再同样从左到右,先叶子后结点的方式,遍历右子树,最后访问根结点。
- 层序遍历,若树为null,则空操作返回,否则从树的第一层根结点开始访问,从上到下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问
案例:
-
先序遍历
-
中序遍历
先递归访问左子树,再访问自身,再递归访问右子树。
-
后序遍历
先递归访问左右子树,再访问自身节点。
④ 单链表操作
带头结点的单链表h为空的判定条件是:C
A.h != NULL;
B.h->next == h;
C.h->next == NULL;
D.h == NULL;
注:不带头结点 则为 D
对于一个具有N个结点的单链表,在给定值为x的结点后插入一个新结点的时间复杂度为 :B
A.O(N^2^)
B.O(N)
C.O(N/2)
D.O(1)
将长度为n的单链表连接在长度为m的单链表之后的算法的时间复杂度为( B )
A.O(n+m)
B.O(m)
C.O(n)
D.O(1)
⑤ 散列表的构造方式
- 直接地址法
- 数字分析法
- 除余数法
- 平方取中法
- 折叠法
2、排序与查找
-
- 先从数列中取出一个数作为基准数。
- 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
- 再对左右区间重复第二步,直到各区间只有一个数。
时间复杂度:
最差 O(n2) 平均 o(nlogn) -
插入排序
大致排序方法与选择排序方法相似。仍然是把将排序的数组分为两个区,无序区和有序区。先把第一个元素列为有序区,拿出无序区第一个元素与有序区的元素进行比较,若小于(大于)有序区的元素,则将它插入该元素的后一个位置。所以,每进行一次操作,无序区减少一个元素,而有序区增加一个元素。循环上面的操作,直到无序区元素为空,则停止该操作,输出有序数组
- 最佳情况:O(n)
- 最坏情况:O(n2)
- 平均情况:O(n2)
-
冒泡排序
简单来说,排序方法就是重复地走过要排序的数列,一次比较相邻的两个元素,如果顺序不满足从小到大(从大到小),就将这两个元素交换,重复地进行,知道没有再需要交换。排序方式:In-place(需要申请额外空间-临时变量)
- 从第一个元素开始比较,比较相邻的元素。如果第一个比第二个大(小),就交换他们两个。
- 对每一对相邻元素做同样的工作,不断重复,直到排序完成。
- 最好情况-- O(n):
初始数组就为从大到小(从小到大),只需要比较 O(n),n为数组的元素。 - 最坏情况–O(n2):
若需要数组为从小到大排序,初始数组为从大到小。最坏的情况就是初始数组为倒序排序,故最坏情况为 O(n2)。所以复杂度为 O(n2) - 稳定性:
稳定。
-
顺序查找
从表的一端开始,顺序扫描线性表,依次将扫描到的关键字和给定值k相比较,若当前扫描到的关键字与k相等,则查找成功;若扫描结束后,仍未找到关键字等于k的记录,则查找失败。
-
最好情况:要查找的第一个就是。时间复杂度为:O(1)
-
最坏情况:最后一个是要查找的元素。时间复杂度未:O(n)
-
平均情况下就是:(n+1)/2。
总的来说时间复杂度为:O(n)
-
-
二分查找
一个有序的序列,取中间元素和目标元素进行对比,取其中的一半,丢弃另一半,快速缩小目标元素所在的位置。主要思想还是:快速缩小目标元素所在的区间。
二分查找的前提条件是有序序列,序列必须是顺序存储元素的。
二分查找的时间复杂度为:O(logn),空间复杂度为:O(1)
三、《数据库系统》
1、数据库设计
① E-R 图
-
组成元素
元素 描述 表示形似 实体 客观存在并可以相互区别的事物 用矩形框,矩形框内写明实体名 属性 实体所具有的一个属性 用椭圆型表示,并用无向边将其与相应的实体连接起来 关系 实体和实体之间以及实体内部的关系 用菱形表示,菱形框内写明联系名,并用无向边分别与有关实体连接起来, -
关系
- 一对一
- 一对多
- 多对多
-
实例详解
问题描述:
- 一个学生可选修多门课,一门课有若干学生选修;
- 一个教师可讲授多门课,一门课只有一个教师讲授;
- 一个学生选修一门课,仅有一个成绩。
- 学生的属性有学号、学生姓名;教师的属性有教师编号,教师姓名;课程的属性有课程号、课程名
② 数据库三大范式
- 第一范式(1NF): 确保每列的原子性(强调的是列的原子性,即列不能够再分成其他几列)。实际上,第一范式是所有关系型数据库的最基本要求。
举例:
第一张表
地址 |
---|
安徽省,合肥市,合肥学院 |
第二张表
省 | 市 | 具体地址 |
---|---|---|
安徽省 | 合肥市 | 合肥学院 |
第一种表设计不满足第一范式,为什么不满足第一范式?因为地址
列不具有原子性,能拆分成省份
、市
和具体地址
;
-
第二范式(2NF): 是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式要求确保表中每列与主键相关,而不能只与主键的某部分相关(主要针对联合主键),主键列与非主键列遵循完全函数依赖关系,也就是完全依赖。
-
第三范式(3NF): 是在第二范式的基础上建立起来的。第三范式指:属性不依赖于其他非主属性
第三范式确保主键列之间没有传递函数依赖关系,也就是消除传递依赖。
③ 数据库约束
- 主键约束:
primary key
当表的某一列被指定为主键之后,该列就不能为空,不能有重复值出现,这样主键才能唯一标识一条记录。 - 非空约束:
not null
某些列不能插入为null值 - 唯一约束:
unique
某些列不能插入重复的值 - 外键约束:
foreign key
用来在两个表数据之间建立链接,其中一张表的一个字段被另一张表中对应的字段约束。也就是说,设置外键约束至少要有两种表,被约束的表叫做从表(子表),另一张叫做主表(父表),属于主从关系。
2、数据库操作
stu表:
列名 | 数据类型 | 宽度 | 为空性 | 说明 |
---|---|---|---|---|
sid | int | Not null | 学号,主键 | |
class | varchar | 10 | Not null | 班级 |
name | varchar | 8 | Not null | 姓名 |
sex | char | 2 | 性别,只可以为”男”或”女” | |
nation | varchar | 20 | 民族 | |
pid | char | 18 | 身份证号,唯一 | |
birthday | smalldatetime | 出生日期 |
cou表:
列名 | 数据类型 | 宽度 | 为空性 | 说明 |
---|---|---|---|---|
cid | int | Not null | 课程号,主键 | |
cname | varchar | 30 | Not null | 课程名 |
semester | char | 1 | 开课学期 | |
hour | int | 学时 |
score表:
列名 | 数据类型 | 宽度 | 为空性 | 说明 |
---|---|---|---|---|
ID | int | Not null | 主键,标识列(1,1) | |
sid | int | Not null | 来自“学生表”关系的外部关键字 | |
cid | int | Not null | 来自“课程表”关系的外部关键字 | |
grade | int |
-
创建数据库
create database yyt
-
打开数据库
use yyt
-
学生表中插入记录
INSERT INTO stu VALUES( 31,'计科','张三','男','汉族','123456777','2002-1-8' )
-
修改数据
update stu set nation='满',birthday='1990-12-29' where sid =45 //按照姓名将某一位同学的民族改为“满”、出生日期改为“1990-12-24”
-
从课程表中查询课程的课程名、开课学期和学时
select cname,semester,hour from cou
-
在学生表中查询年龄为20岁或22岁的学生
select * from stu where (YEAR(GETDATE())-YEAR(birthday)) in (20,22) //GETDATE():系统时间
-
查询学生的学号、姓名、课程名和分数,查询结果按课程名和分数降序排列
select stu.sid,name,cname,grade from stu,cou,score where stu.sid = score.sid and cou.cid=score.cid order by cname,grade desc
-
查询每个班级、每门课程的平均成绩,显示班级、课程名、平均成绩
select class,cname,AVG(grade) from stu,cou,score where stu.sid = score.sid and cou.cid=score.cid group by class,cname
-
删除一名同学的信息通过学号
delete from stu where sid = 45
四、《面向对象程序设计》
1、类与对象
- 对象:静态属性(数据)+ 动态行为(函数)
- 类:类是对象的抽象,而对象是类的具体实例
- 封装与信息隐蔽:把对象中的某些部分对外隐蔽,只留下与外界联系的接口接收外界的消息
- C++ 对象中的公有函数就是对象的对外接口,外界通过调用公有函数,访问对象中的数据成员,完成指定的操作
实操:
建立一个名为Student的类
该类有以下几个私有成员变量:学生姓名、学号、性别、年龄。
还有以下两个成员变量:一个用于初始化学生姓名、学号、性别和年龄的构造函数,一个用于输出学生信息的函数。
编写一个主函数,声明一个学生对象,然后调用成员函数在屏幕输出学生信息。
#include <iostream>
#include <string.h>
using namespace std;
// 建立一个名为Student的类
class Student
{
// 私有成员变量:学生姓名、学号、性别、年龄
private:
string name,sex;
int id,age;
public:
// 初始化学生姓名、学号、性别和年龄的构造函数
Student()
{
name="Wang Fang";
id=234893217;
sex="Male";
age=18;
}
// 用于输出学生信息的函数
void display()
{
cout<<"姓名:"<<name<<endl;
cout<<"学号:"<<id<<endl;
cout<<"性别:"<<sex<<endl;
cout<<"年龄:"<<age<<endl;
}
};
int main()
{
Student stu;
stu.display();
return 0;
}
// 定义成员格式
(1) class 类名 对象名表
例: class student st1, st2;
(2)类名 对象名表
例: student st1, st2;
(3)在声明类类型的同时,定义对象
例: class 类名
{
private:
…
public:
…
} stu1,stu2;
// 类外定义成员函数格式:
类型 类名::函数名(形参)
{
成员声明
}
例:
void Student::display()
{
cout<<"学生"<<endl;
}
2、继承与多态
按解释中的要求在下列程序划线处填入的正确语句是: C
#include <iostream>
#include <string.h>
using namespace std;
class Base{
public:
void fun(){
cout<<"Base::fun"<<endl;
}
};
class Derived:public Base{
public:
void fun(){
//在此空格调用基类的函数 fun()
cout<<"Derived::fun"<<endl;
}
};
A:fun(); B:Base.fun(); C:Base::fun(); D:Base->fun();
#include <iostream>
using namespace std;
class Virtualbase
{
public:
virtual void Demon()= 0;
virtual void Base() {cout<<"1"<<endl;}
};
class SubVirtual :public Virtualbase
{
public:
void Demon()
{
cout<<"2" << endl;
}
void Base()
{
cout<<"3"<<endl;
}
};
int main()
{
SubVirtual* inst = new SubVirtual();
inst->Demon();
inst->Base();
return 0;
}
A. 21
B. 程序报错
C. 3
D. 23 √
下面程序的输出结果是:(B)
class Root {
public:
Root() {
cout << "R1";
}
Root(int) {
cout << "R2";
}
};
class Sub : public Root {
public:
Sub() {
cout << "S1";
}
Sub(int) {
cout << "S2";
}
};
int main()
{
Sub s1(1);
}
A. R1S1
B. R1S2
C. R2S1
D. R2S2
3、UML图
仔细观察以下类图,说法错误的是( )?
- A. 从该类图中可知,SingingSystem类继承Judger类
- B. SingingSystem类共1个属性,7个成员函数 √
- C. Judger类无属性,3个成员函数
- D. 从类图中,无法可知是公有继承,还是私有继承,还是保护继承。
仔细观察以下类图,下列说法中错误的是( )?
- A. 从类图中可知,Cat是派生类,Animal是基类
- B. Cat类共有4个属性,4个成员函数 √
- C. Animal类共有3个属性,3个成员函数
- D. 从该类图中可知,Cat类中的food属性是子类扩展的
仔细观察以下类图,Cat类的成员属性有( C )个?
- A. 3
- B. 1
- C. 4
- D. 7
仔细观察以下类图,子类Son的成员函数有( C )个?
- A. 5
- B. 4
- C. 3
- D. 2