数据结构基础知识核心归纳(一)
转载请声明出处:http://blog.csdn.net/andrexpert/article/details/77900395
Android Java 数据结构
Android基础技术核心归纳(一) Java基础技术核心归纳(一) 数据结构基础知识核心归纳(一)
Android基础技术核心归纳(二) Java基础技术核心归纳(二) 数据结构基础知识核心归纳(二)
Android基础技术核心归纳(三) Java基础技术核心归纳(三) 数据结构基础知识核心归纳(三)
Android基础技术核心归纳(四) Java基础技术核心归纳(四)
不知不觉又是一年的9月,今天跟一个师弟聊天,谈到了他现在面试的一些情况,突然想起自己当年也是这么走过来的,顿时感慨良多。Android/Java经验汇总系列文章,是当初自己毕业时笔试、面试和项目开发中相关的总结,虽然不是很高深的东西,也没有归纳得很全面,但是对Android、算法、Java把握个大概还是没问题,今天特意将这些文章放出来,希望能够对看到这个系列文章的毕业生朋友一点帮助吧。当然,由于受当时知识面的限制,归纳得可能不是很准确,若有疑问就留言哈。
1.浅谈栈、队列、堆的区别?
1.堆:堆是一种树状的数据结构。一般由程序员分配释放,存放由new创建的对象和数组(C中是由malloc分配和free释放),JVM不定时查看这个对象,如果没有引用指向这个对象就回收.
(1)优点:可动态分配内存大小,生成周期不必事先告诉编译器,Java垃圾回收自动回收数据;
(2)缺点:运行时需动态分配内存,因此,数据存储速度较慢
2.栈:是一种仅允许在一端进行插入和删除的线性表,即先进后出。由编译器自动分配释放,存放函数的参数值、局部变量的值等基本类型的变量和对象的引用。
(1)优点:存储速度比较块,仅次于寄存器且栈数据可以共享;
(2)缺点:存在栈中的数据大小与生存期必须是确定的(int y=4,y在栈中占4字节),缺乏灵活性
3.队列:是一种仅允许在尾端进行插入数据元素,首端进行删除数据元素的线性表,即先进先出FIFO。在队列中,数据元素可以任意增减,但数据元素的次序不会改变。每当有数据元素从队列中被取出,后面的数据元素一次向前移动一位。
参考:http://www.sxt.cn/u/1349/blog/2330
2.浅谈数组与链表的区别?
(1)从逻辑结构来看:数组必须实现定义固定的长度(元素个数),不能适应数据动态地增减的情况。当数据增加时,可能会造成溢出;当数据减少时,造成内存浪费,数组中插入、删除数据项时,需要移动其他数据项。链表动态地进行存储分配,可以使用数据动态地增减情况,可以节约内存,且可以方便地插入、删除数据数据项。
(2)从内存存储来看:(静态)数组从栈中分配空间(注:用new创建的在堆中分配),存储速度快但缺乏灵活性;链表从堆中分配空间,灵活性好但是申请管理比较麻烦;
(3)从访问方式来看:数组在内存中是连续存储的,因此可利用下标索引进行随机访问;链表是链式存储结构,在访问元素的时候只能通过线性的方法由前到后顺序访问,所以访问效率比数组要低。
3.单链表的创建、插入和删除操作?
1.单链表创建、插入、删除
(1)结点结构体
/***struct Node *即存储的是类型数据地址
而后继结点即为struct Node类** */
typedef struct
{
int data; //结点数据域 ,存放数据
struct Node *next; //结点指针域,指针变量next存放struct Node *类型变量存储地址(指向结点)
}Node,*LinkList; //Node表示结点,*LinkList 定义一个链表
(2)单链表读取操作
/*功能:从单链表中读取第i个元素,1<=i<=ListLenght(L)
存放到指针e指向的存储地址中*/
#define UNSUCCESS -1
#define SUCCESS 1
typedef Status int;
Status GetElem(LinkList L,int i,int *e)
{
int j=1;
LinkList p; //声明一个结点p
p=L->next; //让结点p指向链表L的第一个结点
while(p&&jnext; //让p指向下一个结点,
++j;
}
if(!p || j>i) //当p指向null或者j大于要查询的位置时,查询失败
return UNSUCCESS ;
*e=p->data; //查找成功,将链表中的第i个元素的数据存储到指针e指向的空间
return SUCCESS;
}
(3)单链表插入操作
/*功能:将元素e插入到单链表的第i个位置*/
#define UNSUCCESS -1
#define SUCCESS 1
typedef Status int;
Status ListInsert(LinkList *L,int i,ElemType e)
{
int j=1;
LinkList p,s; //声明一个结点p
p=*L; //让结点p指向链表L的第一个结点
while(p&&jnext; //让p指向下一个结点
++j;
}
if(!p || j>i) //当p指向null或者j大于要查询的位置时,查询失败
return UNSUCCESS ;
s=(LinkList)malloc(sizeof(Node)); //生成新结点,即为新结点开辟一段内存
s->data=e; //查找成功,将数据元素e赋值给s结点的数据域
s->next =p->next;//将结点p的后继结点赋值给s的后继(p->next为指向结点p下一个存储地址所在的结点)
p->next=s; //设置结点p的后继结点为结点s
return SUCCESS ;
}
(4)单链表删除操作
/***功能:删除单链表L中的第i个数据元素(结点)
并将该数据元素存储到指针变量e指向的存储空间中***/
#define UNSUCCESS -1
#define SUCCESS 1
typedef Status int;
Status ListInsert(LinkList *L,int i,ElemType *e)
{
int j=1;
LinkList p,q; //声明一个结点p
p=*L;
//从链表第一个结点开始遍历,直到j=i-1,让p指向存储i-1位置所在的结点
while(p->next&&jnext; //让p指向下一个结点
j++;
}
if(!p || j>i)
return UNSUCCESS;
q=p->next; //设置q为p的后继结点,即将第i个结点的地址保存到q
p->next=q->next; //将q的后继结点设置为p的后继结点
*e=q->data; //将q结点中的数据给e
free(q); //设置完成后,让系统回收此结点,释放内存
return SUCCESS;
}
注意:LinkList s为定义一个结点,同数组名一样,都可以直接用于表示地址。
/***
随机产生n个元素的值,建立带头结点的单链线性表L(头插法)***/
//单链表节点的定义
typedef struct{
int data;
struct Node* next;
}Node;
//由一个数组创建单链表
Node* CreateList(int *a, int count)
{
if(NULL == a)
return NULL;
//头结点:开辟空间,赋值数据域、指针域
Node* head = (Node*)malloc(sizeof(Node));
head->data= a[0];
head->next = NULL;
//将链表的头结点赋值给指针变量p
Node* p = head;
for (int i = 1; i < count; ++i)
{
p->next = (Node*)malloc(sizeof(Node));
p->next->data= a[i];
p->next->next = NULL;
p = p->next;
}
return head; //返回链表
}
然后,在main函数中,实现如下代码:
p=rearA->next; //声明一个结点p,用来保存A表的头结点
rearA->next=rearB->next->next;
//将本指向B表的第一个结点(不是头结点)赋值给rearA->next(原本指向A表的头结点)
rearB->next=p; //将原A表的头结点赋值给rearB->next
free(p); //释放p,因为p结点会占用内存资源
p->prior->next=p->next; //把p->next赋值给p->prior的后继
p->next->prior=p->prior; //把p->prior赋值给p->next的前驱
free(p); //释放结点p占用的内存空间
5.单链表逆序(反转)与链表存在环路的判断(快慢指针)?
head->next = prev; //将A结点的next指向prev
prev = head; //将指针prev指向A结点
head = next; //将指针head指向B结点
next = head->next; //将指针next(或head->next)指向C结点
(2)算法实现:
61 LINK_NODE *ReverseLink(LINK_NODE *head) //结点类型指针变量head指向单链表头结点
62 {
63 LINK_NODE *next;
64 LINK_NODE *prev = NULL; //初始条件
66 while(head != NULL) //终止条件
67 {
68 next = head->next; //指针next指向head->next结点
69 head->next = prev;
70 prev = head;
71 head = next;
72 }
74 return prev;
75 }
2..链表存在环路的判断
int RingJudge(LINK_NODE *head){
LINK_NODE *p,*q;
/* 链表的头指针为h */
if((NULL == head) || (NULL == head->next)) /* 头指针为空或者链表中只有一个节点,则无环,退出 */
{
return 0;
}
p = q = head; /* 设p和 q 指针, 均指向头结点 */
while(1)
{
p = p->next;
q = (q->next)->next;
if((NULL == p) || (NULL == q))
{
printf(“No Ringn”); /* 链表中无环, 退出 */
return 0;
}
if(p == q) /* 链表中有环 */
{
printf(“Ring occurred\n”);
return 1;
}
}
}
参考:http://blog.csdn.net/autumn20080101/article/details/7607148
LINK_NODE *GetKElem(LINK_NODE *head,int k,ElemType *e){
if(head == null){ //空链表
return null;
}
LINK_NODE *first,*second; //声明俩个个指针,初始同时指向头结点
first = second = head;
/*
*(1)判断k是否出界,若没出界则将指针first移动k个位置
*/
while(k != 0){
if(first == null) //链表元素个数小于k个
return null;
first = first->next;
k--; //指针first从头结点开始移动k个位置
}
/*
*(2)判断first是否到底,若没指针second开始从头指针移动,指针first从第k位置移动
* 当first->next直到指向Null时,循环结束,指针second指向单链表的第K位置
*/
while(first != null){
first = first->next;
second = second->next;
}
if(second != null && second != head) //将单链表的第K个位置的元素数据保存到指针e指向的地址空间
*e = second->data;
return second;
}
7.单链表排序?时间复杂度[O(nlogn)],空间复杂度为常量
LinkNode* SortLinkListInsertion(LinkNode* head)
{
//链表空或链表只有一个节点,不必排序,直接返回原头结点
if (NULL == head || NULL == head->next){
return head;
}
//我们从第二个节点进行处理,第一个节点作为结果链表的初始节点
LinkNode* r = head->next;
LinkNode* tmp;
head->next = NULL; //将结果链表末尾标记为结束
while(NULL != r) { //依次处理每一个节点
if (r->data < head->data) {
//将该节点插到结果链表最前,并修改链表头,同时注意辅助变量的使用
tmp = r->next;
r->next = head;
head = r;
r = tmp;
}
else{
//否则从链表头部开始搜索,注意这里搜索结束的条件是当前被搜索节点不是最后一个节点
LinkNode* p = head;
while(NULL != p->next){
//注意只有当节点严格小于被搜索节点的下一节点的值是,才将其插入到被搜索节点后
//这样能保证排序是稳定的!
if (r->data < p->next->data) {
tmp = r->next;
r->next = p->next;
p->next = r;
r = tmp;
continue; //注意跳出,开始处理下一个节点
}
else {
p = p->next;
}
}
//此时,p是结果链表中的末尾节点,将被处理节点追加到其后
if (NULL == p->next){
tmp = r->next;
r->next = NULL;
p->next = r;
r = tmp;
}
} //end else
} //end while(NULL != r)
return head;
}
2.选择排序(以从小到大排序为例)
LinkNode* SortLinkListSelection2(LinkNode* head)
{
//我们这里即使不进行特殊情况处理,代码也能正常工作,可以代入检查
//if (NULL == head || NULL == head->next)
//{
// return head;
//}
LinkNode* p = NULL; //遍历辅助变量
LinkNode* pminpre = NULL; //指向源链表中除头结点外的最小值节点的前驱节点
LinkNode L = {0, NULL}; //我们这里用了一个哑节点,它能简化后面的代码
LinkNode* Ltail = &L; //Ltail用于指向结果链表的最后一个节点
while (NULL != head && NULL != head->next) //循环处理源链表中节点个数不小于2个的情况
{
pminpre = head;
p = head->next;
while(NULL != p && NULL != p->next) //找出源链表中除头结点外的最小值节点的前驱节点
{
if (p->next->val < pminpre->next->val) //严格小于时才改变pminpre
pminpre = p;
p = p->next;
}
if (head->val <= pminpre->next->val) //和头结点值进行比较处理,值相等时,取头结点
{
Ltail = Ltail->next = head;
head = head->next;
}
else
{
Ltail = Ltail->next = pminpre->next;
pminpre->next = pminpre->next->next;
}
}
Ltail = Ltail->next = head; //最后一个节点直接追加到结果链表的尾部
Ltail->next = NULL; //设置结果链表的结束标记
return L.next;
}
注意上面if语句中的 < 和 <= 判断,他们能使得链表的选择排序是稳定的排序。
//asscending sort link list
LinkNode* SortLinkListBubble(LinkNode* head)
{
if (NULL == head)
{
return head;
}
//init end pointer
LinkNode* end = NULL;
while(true)
{
LinkNode* n = head->next;
if (n == end)
break;
//这里单独处理头结点和第二个节点的比较,这是没有哑头节点的链表的一个劣势
if (n->val < head->val)
{
head->next = n->next;
n->next = head;
head = n;
}
LinkNode* pre = head;
LinkNode* cur = head->next;
LinkNode* tmp;
n = cur->next;
while(n != end)
{
if (n->val < cur->val)
{
tmp = n->next;
cur->next = n->next;
n->next = cur;
pre->next = n;
n = tmp;
}
else
{
n = n->next;
}
pre = pre->next;
cur = cur->next;
}
end = cur;
}
return head;
}
4.快速排序(从小到大排序)
void QsortList(LinkNode*& head, LinkNode* end)
{
if(head == NULL || head == end)
return;
LinkNode* pivot = head;
LinkNode* p = head->next;
LinkNode* head1 = NULL, *tail1 = NULL;
LinkNode* head2 = NULL, *tail2 = NULL;
while(p != end)
{
if(p->val < pivot->val)
{
if(head1 == NULL)
{
head1 = tail1 = p;
}
else
{
tail1->next = p;
tail1 = p;
}
}
else
{
if(head2 == NULL)
{
head2 = tail2 = p;
}
else
{
tail2->next = p;
tail2 = p;
}
}
p = p->next;
}
if (tail1)
tail1->next = pivot;
if (head2)
pivot->next = head2;
else
pivot->next = end;
if (tail2)
tail2->next = end;
if (head1)
head = head1;
else
head = pivot;
QsortList(head, pivot); //这里是传入head, 而不能传入head1,因为head还可能被子调用修改
//同样这里传入pivot->next而非head2,这样才能保证最后链表是有序链在一起的
QsortList(pivot->next, end);
}
调用QsortList时采用这样的方式QsortList(L, NULL);
void Locate(int &x)
{
< 结点类型说明 >
//遍历链表,找到元素值为x的结点
*p = first->next;
while (p != first && 1 p->data!=x )
p = p->next;
if (p != first)
{
2 p->freq++; //找到结点后,将结点的访问频率freq加1
< 结点类型说明 >
*current = p;
current->prior->next = current->next;
current->next->prior = current->prior;
p = current->prior;
//向前遍历链表,链接到现它的访问频度相等的结点后面
while (p != first && 3 p->freq<=x)
p = p->prior;
current->next = 4 p->next;
current->prior = p;
p->next->prior = current;
p->next = 5 current;
}
else
printf(“Sorry. Not find!\n”); \*没找到*\
}