1、B/S与C/S的区别
B/S:即Browser/Server(浏览器/服务器)结构,就是只安装维护一个服务器(Server),而客户端采用浏览器(Browse)运行软件。
主要特点是交互性强、具有安全的存取模式、网络通信量低、响应速度快、利于处理大量数据。但是该结构的程序是针对性开发,变更不够灵活,维护和管理的难度较大。通常只局限于小型局域网,不利于扩展。并且,由于该结构的每台客户机都需要安装相应的客户端程序,分布功能弱且兼容性差,不能实现快速部署安装和配置,因此缺少通用性,具有较大的局限性。
C/S:CS即Client/Server(客户机/服务器)结构。
主要特点是分布性强、维护方便、开发简单且共享性强、总体拥有成本低。但数据安全性问题、对服务器要求过高、数据传输速度慢、软件的个性化特点明显降低。
2、Linux软链接与硬链接的区别
硬链接(hard-link):若A是B的硬链接,则A与B指向的是相同的inode节点。这里的“硬”我理解为物理上的,指向同一块存储地址。两个文件名指向同一个文件,A和B对文件系统来说是完全平等的。如果删除了其中一个,对另外一个没有影响。每增加一个文件名,inode节点上的链接数增加一,每删除一个对应的文件名,inode节点上的链接数减一,直到为0,inode节点和对应的数据块被回收。注:文件和文件名是不同的东西,rm A删除的只是A这个文件名,而A对应的数据块(文件)只有在inode节点链接数减少为0的时候才会被系统回收。
由于硬链接是有着相同 inode 号仅文件名不同的文件,因此硬链接存在以下几点特性:
1、文件有相同的 inode 及 data block;
2、只能对已存在的文件进行创建;
3、不能交叉文件系统进行硬链接的创建;
4、不能对目录进行创建,只可对文件创建;
5、删除一个硬链接文件并不影响其他有相同 inode 号的文件。
软链接(soft-link):又称符号链接,若A是B的软连接,则A的数据区存放的就是B的文件路径。这里的“软”我理解为逻辑上的。
软链接有着自己的 inode 号以及用户数据块。因此软链接的创建与使用没有类似硬链接的诸多限制:
1、软链接有自己的文件属性及权限等;
2、可对不存在的文件或目录创建软链接;
3、软链接可交叉文件系统;
4、软链接可对文件或目录创建;
5、创建软链接时,链接计数 i_nlink 不会增加;
6、删除软链接并不影响被指向的文件,但若被指向的原文件被删除,则相关软连接被称为死链接(即 dangling link,若被指向路径文件被重新创建,死链接可恢复为正常的软链接)。
相关命令:
硬:ln 源文件 链接名
软:ln -s 源文件 链接名
3、Linux进程内存空间各段分布(stack ,heap,bss segment,code segment/text segment,data segment)
以4G内存为例代码段:代码段是用来存放可执行文件的操作指令,也就是说是它是可执行程序在内存种的镜像。代码段需要防止在运行时被非法修改,所以只准许读取操作,而不允许写入(修改)操作——它是不可写的。
数据段:数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量和全局变量。
BSS段:BSS段包含了程序中未初始化全局变量,在内存中bss段全部置零。
堆(heap):堆是用于存放进程运行中被动态分配的内存段,它大小并不固定,可动态扩张或缩减。当进程调用malloc/new等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
栈(stack):栈是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味这在数据段中存放变量)。除此以外在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也回被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上将我们可以把堆栈看成一个临时数据寄存、交换的内存区。
4、快速排序
核心思想是找到一个基准值,使得其左边的值都小于它,右边的都大于它(实质是每次排序都让一个值处于正确的位置)。这样划分一次后产生两个区间,再分别对这两个区间做同样的事。
假设对序列7,15,8,54,2,6,9进行快速排序,以第一个数7作为基准数,分别设两个指针left、right,left指向基准数后面一个数,right指向最后一个数。
1、从右边开始,找到第一个比基准数小的数(这里为6),用right标记位置
2、从左边开始,找到第一个比基准数大的数(这里为15),用left标记位置,注意,right不能越过left的位置,若相交,跳到第4步
3、交换left与right对应的值,right-1,left+1
4、重复上面的步骤,直到left=right,将基准数与right标记的数交换位置,第一轮交换结束
接下来对小于基准数的部分和大于基准数的部分都做这个操作。
Java代码(引自最通俗易懂的快速排序算法详解 - yugenhai - 博客园)
public static int Partition(int[] a, int p, int r) {
int x = a[r - 1];
int i = p - 1;
int temp;
for (int j = p; j <= r - 1; j++) {
if (a[j - 1] <= x) {
// 交换(a[j-1],a[i-1]);
i++;
temp = a[j - 1];
a[j - 1] = a[i - 1];
a[i - 1] = temp;
}
}
// 交换(a[r-1,a[i+1-1]);
temp = a[r - 1];
a[r - 1] = a[i + 1 - 1];
a[i + 1 - 1] = temp;
return i + 1;
}
public static void QuickSort(int[] a,int p,int r){
if(p < r) {
int q = Partition(a, p, r);
QuickSort(a, p, q-1);
QuickSort(a, q + 1, r);
}
}
// main方法中将数组传入排序方法中处理,之后打印新的数组
public static void main(String[] stra) {
int[] a = { 7, 10, 3, 5, 4, 6, 2, 8, 1, 9 };
QuickSort(a, 1, 10);
for (int i = 0; i < a.length; i++)
System.out.print(a[i]+" ");
}
5、二叉树的遍历
前序遍历:根结点 ---> 左子树 ---> 右子树
中序遍历:左子树--->根结点 ---> 右子树
后序遍历:左子树 ---> 右子树 ---> 根结点
已知3种遍历序列中任意两个,即可还原出二叉树。
引自已知二叉树的前序遍历和中序遍历,如何得到它的后序遍历? - 经欢非常道 - 博客园
利用以下几个特性:
特性A,对于前序遍历,第一个肯定是根节点;
特性B,对于后序遍历,最后一个肯定是根节点;
特性C,利用前序或后序遍历,确定根节点,在中序遍历中,根节点的两边就可以分出左子树和右子树;
特性D,对左子树和右子树分别做前面3点的分析和拆分,相当于做递归,我们就可以重建出完整的二叉树;
我们以一个例子做一下这个过程,假设:
前序遍历的顺序是: CABGHEDF
中序遍历的顺序是: GHBACDEF
第一步,我们根据特性A,可以得知根节点是C,然后,根据特性C,我们知道左子树是:GHBA,右子树是:DEF。
第二步,取出左子树,左子树的前序遍历是:ABGH,中序遍历是:GHBA,根据特性A和C,得出左子树的父节点是A,并且A没有右子树。
第三步,使用同样的方法,前序是BGH,中序是GHB,得出父节点是B,B的左子树是GH,且B没有右子树。
第四步,GH的前序是GH,中序是GH,得出父节点是G,G没有左子树,右子树为H。
第五步,回到右子树,它的前序是EDF,中序是DEF,依然根据特性A和C,得出父节点是E,左右节点是D和F。
到此,我们得到了这棵完整的二叉树。因此,它的后序遍历就是:HGBADFEC。
6、Java关键字synchronized
synchronized关键字最主要有以下3种应用方式,下面分别介绍
修饰实例方法,作用于当前实例加锁,进入同步代码前要获得 当前实例 的锁
所谓的实例对象锁就是用synchronized修饰实例对象中的实例方法,注意是实例方法不包括静态方法
修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得 当前类对象 的锁
当synchronized作用于静态方法时,其锁就是当前类的class对象锁。由于静态成员不专属于任何一个实例对象,是类成员,因此通过class对象锁可以控制静态成员的并发操作。需要注意的是如果一个线程A调用一个实例对象的非static synchronized 方法,而线程B需要调用这个实例对象所属类的静态 synchronized方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的 class 对象,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得 给定对象 的锁
除了使用关键字修饰实例方法和静态方法外,还可以使用同步代码块,在某些情况下,我们编写的方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会得不偿失,此时我们可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了。
网友评论