if((s= (student *) malloc(sizeof(student)))==NULL) { /*分配新存储空间并检测*/
printf("不能分配内存空间!
");
exit(0);
}
p->link=s; /*把s的地址赋给p所指向的结点的链域,这样就把p和s所指向的结点连接起来了*/
printf("请输入第%d个人的姓名",i+1);
//指向结构体中的数据
scanf("%s",s->name);
s->link=NULL;
p=s;
}
//如果是单向链表,这里为NULL,环形链表需要指向保存表头节点的指针。
p->link=h;
return(h);
}
int main(void)
{
int number;
student *head; /*head是保存单链表头结点地址的指针*/
student *p;
printf("请输入相应的人数:
\n");
scanf("%d",&number);
head=create(number); /*把所新建的单链表头地址赋给head*/
p=head;
while(p->link) {
printf("%s\n",p->name);
p=p->link;
}
return 0 ;
}
5.有两个有序链表,合并成一个有序链表
#include
typedefstructNode
{
intvalue;
structNode*next;
};
Node *ListMerge(Node *head1,Node *head2)
{
Node *head=NULL;//合并后的头指针
Node*p=NULL;//永远指向最新合并的结点
Node *p1=head1;//p1用于扫描链表1
Node *p2=head2;//p2用于扫描链表2
if(!
head1) return head2;
if(!
head2) return head1;
if(head1->valuevalue) //升序排列
{
head=head1;
p1=head1->next;
}
else
{
head=head2;
p2=head2->next;
}
Node *p=head;//p永远指向最新合并的结点
while(p1 && p2)//如果循环停止,则p1或p2至少有一个为NULL
{
if(p1->valuevalue)
{
p->next=p1; //链接最新的节点
p1=p1->next; //遍历下一个节点
}
else
{
p->next=p2; //链接最新的节点
p2=p2->next; //遍历下一个节点
}
p=p->next; //指向最新的节点
}
if(p1){//如果链1还没走完
p->next=p1;
}
elseif(p2){//如果链2还没走完
p->next=p2;
}
return head;
}
6.单向链表和双向链表区别和应用场合:
双向链表其实是单链表的改进。
当我们对单链表进行操作时,有时你要对某个结点的直接前驱进行操作时,又必须从表头开始查找。
这是由单链表结点的结构所限制的。
因为单链表每个结点只有一个存储直接后继结点地址的链域,那么能不能定义一个既有存储直接后继结点地址的链域,又有存储直接前驱结点地址的链域的这样一个双链域结点结构呢?
这就是双向链表。
在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点地址,一般称之为右链域;一个存储直接前驱结点地址,一般称之为左链域。
7.排序
重点冒泡法:
void Bubble_Sort(int *num, int n)
{
int i, j;
for(i = 0; i < n-1; i++){//n个数,要进行n-1次比较,因为第一个数不用和自身比较.
for(j = 0; i + j < n - 1; j++){//他外层每循环一遍,就有一个最大的数跑到了最右端,因此剩下要排的数就减1
if(num[j] > num[j + 1]){
int temp = num[j];
num[j] = num[j + 1];
num[j + 1] = temp;
}
}
}
}
8.&是什么作用呢?
让某一位清零,也就是最低位清零,而其他位保持不变,|呢?
让某一位置1,其他保持不变,^是异或运算。
9.Q化运算Q15?
表示范围-1≤X≤0.9999695Q31?
不同的Q所表示的数不仅范围不同,而且精度也不相同。
Q越大,数值范围越小,但精度越高;相反,Q越小,数值范围越大,但精度就越低。
10.堆,栈:
stack又称系统栈(systemstack),用于:
保存函数调用后的返回地址;给局部变量分配存储空间;传递函数参数;保存临时结果;heap-编译器提供的运行时支持库的一些函数(如malloc/calloc/realloc),允许运行时为变量动态分配存储器。
这些存储器就放置在.system段的全局池(globalpool)或堆(heap)中。
这个动态存储池的大小仅仅受限与系统中实际的存储容量。
这2个选项都可以在project-buildoptions的连接器选项中设置。
11.输出格式:
%d十进制;%o八进制;%x十六进制;%c输出一个字符;%s输出一个字符串;%f输出一个小数;
12.动态申请内存(一维数组,二维数组)
int * p = new int[length];一维数组delete[] p;
//动态开辟空间
int **p = new int*[m]; //开辟行 //释放开辟的资源
for(int i = 0; i < m; i++) for(i = 0; i < m; i++) delete[] p[i];
p[i] = new int[n]; //开辟列 delete[] p;
int*sort;
sortlong=x+y+k-3;
sort=(int*)malloc(sortlong*sizeof(int));
此时sort为一个长度是sortlong的数组的数组名,可以把sort当普通数组进行处理
13.大端模式、小端模式:
1)Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
2)Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
C++多态的实现及原理:
C++的多态性用一句话概括就是:
在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。
如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数
1:
用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数。
2:
存在虚函数的类都有一个一维的虚函数表叫做虚表,类的对象有一个指向虚表开始的虚指针。
虚表是和类对应的,虚表指针是和对象对应的。
3:
多态性是一个接口多种实现,是面向对象的核心,分为类的多态性和函数的多态性。
4:
多态用虚函数来实现,结合动态绑定.
5:
纯虚函数是虚函数再加上=0;
6:
抽象类是指包括至少一个纯虚函数的类。
14.引用和指针的区别:
指针:
指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。
指针是用来指向某个变量,而引用是给变量取个别名,其作用就如同typedef一样。
用引用作形参时在调用函数里就像操作实参一样,不需要考虑实参的地址问题。
用指针做形参时,由于指针的值是变量的地址,所以要通过对地址解引用来操作其所指的变量。
在C++里优先选择引用类型作为形参,因为操作一个变量比操作一个指针要简单的多
但用指针作为形参的好处是它可以通过自增或自减改变它的指向。
在函数内部修改形参所指向的地址,调用完成后,传指针不会改变实参指向的地址,传引用的会改变实参指向的地址!
对于引用,有以下三条规则:
(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
(2)不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
15.关于指针:
定义一个数组指针:
//数组指针也称为指向一维数组的指针,也称为行指针
inta[3][4];
int(*p)[4];//该语句定义一个数组指针,指向含4个元素的一维数组
p=a;//该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
定义一个指针数组:
int*p[n]//[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素
定义一个函数指针:
一个函数在编译时被分配一个入口地址。
这个入口地址就称为函数指针。
可以用一个指针变量指向函数,然后通过该指针变量调用此函数。
函数指针有两个用途:
调用函数和做函数的参数。
intmax(intx,inty)
{
intz;
if(x>y)z=x;
elsez=y;
return(z);
}
intmain()
{
intmax(int,int);
int(*p)(int,int);用(*p)来替代函数名
inta,b,c;
p=max;
scanf("%d,%d",&a,&b);
c=(*p)(a,b);
printf("a=%d,b=%d,max=%d\n",a,b,c);
return0;
}
定义一个函数的指针数组:
intadd1(inta1,intb1);
intadd2(inta2,intb2);
intmain(intargc,char*argv[])
{
intnuma1=1,numb1=2;
intnuma2=2,numb2=3;
int(*op[2])(inta,intb);//指针变成了指针数组
op[0]=add1;
op[1]=add2;
printf("%d%d\n",op[0](numa1,numb1),op[1](numa2,numb2));
getch();
}
intadd1(inta1,intb1)
{
returna1+b1;
}
intadd2(inta2,intb2)
{
returna2+b2;
}
指针void是一种特殊类型的指针。
void指针可以指向任意类型的数据,可以是整数,浮点数甚至字符串。
唯一的限制是被指向的数值不可以被直接引用(不可以对它们使用引用星号*),因为它的长度是不定的,因此,必须使用类型转换操作或赋值操作来把void指针指向一个具体的数据类型。
给空指针指向的地址赋值是错的,程序会崩溃。
16.二维数组哪个可以留空?
定义数组时对第一维的长度可以不指定,但第二维的长度不能省,因为系统会根据总个数和第二维的长度计算出第一维的长度。
因为二维数组是由若干个一维数组组成的,在内存中数组是按行存放的,因此,在定义二维数组时必须指定列数。
17.内联函数和宏定义的区别:
内联函数和普通函数相比可以加快程序运行的速度,因为不需要中断调用,在编译的时候内联函数可以直接被镶嵌到目标代码中。
避免一些不必要拷贝和构造,提高工作效率
而宏定义就是一个替代,相当于一个字符串
18.二叉树搜索遍历结果
19.二叉树的节点问题
完全二叉树:
叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。
20.为什么不采用递归法:
递归的效率低,主要是因为递归时会反复调用函数(递归函数)而函数发生调用时,程序会将当前的一些数据存入堆栈中(保存现场),等函数调用结束时,将堆栈中的数据再取出来(恢复现场)。
函数调用时,是有开销的(占用堆栈资源,占用CPU时间)。
无节制的递归会造成堆栈溢出
21.Dijkstra算法步骤第几步问题
Dijkstra算法是典型最短路算法,用于计算一个节点到其他所有节点的最短路径。
主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。
Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。
22.常见算法
排序的稳定性:
若一个序列里面有重复的项,排完序这几个重复的项的相对位置没有发生变化,就是稳定的。
不稳定排序算法:
“快些选堆”“快”指快速排序,“些”指希尔排序,“选”指选择排序,“堆”指堆排序。
其他自然都是稳定的。
交换类排序:
包括冒泡法排序以及快排
快速排序:
快速排序(Quicksort)是对冒泡排序的一种改进。
它的基本思想是:
选定一个key值,通过一趟排序将要排序的数据分割成key值两侧独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
代码:
要明确递归调用结束条件,其中left>=right时退出函数。
对于void函数,return;表示退出该函数。
选择排序:
它的工作原理是每一次从无序组的数据元素中选出最小(或最大)的一个元素,存放在无序组的起始位置,无序组元素减少,有序组元素增加,直到全部待排序的数据元素排完。
选择排序核心思想:
从未排好的部分的第一个作为最小(最大)的,然后依次和剩余的比较,如果有更小(更大)的,记下下标,以此作为新的最小(最大)值,继续比较,一趟结束后,然后和第一个进行交换。
代码如下:
voidSelectionSort(intarray[],intlength)
{
inti,min,j,temp=0;
for(i=0;imin=i;//每次讲min置成无序组起始位置元素下标
for(j=