如何用二叉树存储大量数据?特别求助

学习爱好者777 2019-04-06 02:28:56
书上在讲解二叉树的时候,一般都用几个数据进行演示,比如五个十个数据,接着就会考虑如果存储大数据的情况下,会不会栈溢出?这个问题很长时间都在困惑我,特别想听听大家的看法。

下面用Java的数据结构来说明(C也类似)

就以二叉查找树来说,它的insert()算法如下:
	private BinaryNode<E> insert(E e, BinaryNode<E> t) {
if (t == null)
return new BinaryNode<E>(e, null, null);
int compareResult = e.compareTo(t.element);
if (compareResult < 0) {
t.left = insert(e, t.left);
} else if (compareResult > 0) {
t.right = insert(e, t.right);
} else
;
return t;
}


这里面用的是递归,假定有十万条数据,树已经很高的情况下,即使它查找的再快,insert语句也一定递归的次数非常多,会不会导致栈溢出?
我的问题归结就是两个:
一、如果这种算法不能用于大量数据存储,那它的作用是什么?
二、真正实际用的数据库如果用这种方式来存储,它的实际用法是怎么样的?

同时我也问下数据库存储大量数据的思路和原理


...全文
333 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq_39129460 2019-10-14
  • 打赏
  • 举报
回复
我怎么感觉说的 他说的又问题啊 二叉树 不应该有多少个结点 递归多少次吗 为啥他说的有多少层递归多少次
学习爱好者777 2019-09-17
  • 打赏
  • 举报
回复
引用 16 楼 lx3275852 的回复:
谢谢,但不用了,我对这些也没兴趣。

老师对啥有兴趣?我觉得多线程是一个比较难又重要的一个问题。现在面临的几个难题都和这有关,还牵涉C++11,已经焦头烂额。。
lx3275852 2019-08-29
  • 打赏
  • 举报
回复
谢谢,但不用了,我对这些也没兴趣。
学习爱好者777 2019-08-29
  • 打赏
  • 举报
回复
引用 13 楼 lx3275852 的回复:
我都不知道你从哪看的资料,冒出这么多概念,我都不知道UI线程、工作线程,还去简单查了一下,好像是MFC的概念……
我估计不能准确回答吧,但简单给你个指引。


大牛看下这些库文件的列表,层次还是比较清晰,分为两类,一类是线程库定义,一类是消息循环定义。此外,还引用到单例和锁等,相对这些并不是非常重要,就没贴上来。

对于线程库分为四种类
1.ThreadManager是对线程的总管理,可以投送之类。(我不知道它是怎么管理的,也不知道它是怎么驱动投送的?)
2. local大概就是线程局部数据的定义,TLS的概念 (这个对现在的程序不是很重要)
3.Thread是线程基类,有一个Run,因为是基类所以比较简单,这个类我看明白了。
4.framework_thread就是框架线程定义,是一个实现了很多功能的用于框架的类,framework继承了Thread,而 程序则继承框架线程类工作。

对于消息循环有三种,上面的文章也写了一些,主要分为UI消息循环,IO消息循环和普通消息循环。

一共就这几个文件,数量不多,可是没看懂。
如果大牛有时间,我可以把这个框架发给你看下,体积不大,只有几十K。
学习爱好者777 2019-08-29
  • 打赏
  • 举报
回复
引用 13 楼 lx3275852 的回复:
我都不知道你从哪看的资料,冒出这么多概念,我都不知道UI线程、工作线程,还去简单查了一下,好像是MFC的概念……
我估计不能准确回答吧,但简单给你个指引。

大牛,谢谢你的回复。这个问题我理解了,其实主要就是一个区别,只有UI线程有消息队列,而工作队列没有消息队列。
简洁概括就是,哪个线程使用createwindow创建了窗口,哪个线程就是UI线程,而它就有消息队列。(windows环境,是windows给它提供的消息队列)
————————————————————————————————

这么长时间一直在向大牛请教,真的非常感谢。这几天我碰到一个终极难题,算是对这些知识的总概括。
这个问题就是多线程框架。我想请教这个问题,因为这个问题太重要了,无处不在使用。

问题源于我看的一个程序,引用了谷歌的base库,这个库就是内存,线程,工具类等等的一个套装,其中里面有一个多线程库。
下面是一篇关于这个线程库的描写
https://blog.csdn.net/ilzx53xx0/article/details/77896978

它的意思大概是把多线程库划分成三类,UI线程,IO线程和通用线程。并且还涉及到C++11闭包投放Task,线程管理器ThreadManager等等。

而在实际的程序中,还涉及到窗口生成等等,这个库的资料很少,网络上很难找到。我现在读的云里雾里,但是程序还要继续前进,在这块上卡的太厉害,实在找不到能问的地方。虽然代码摆在面前,读的也很痛苦,主要是因为我没有一个原理和流程上的轮廓。

我想请教的是,大牛能不能把这个原理从起源到框架的生成,这中间的演化过程说下。写的越通俗的越好,就是一个大轮廓的概括,不用写的特别细,就是它怎么运转的就可以。比如这个多线程框架是如何和生成的窗口进行通信的,如何投送Task等,主要是大概原理。
————————————————————————————————
我理解多线程,锁,UI线程和工作线程等基础概念,做了一定程度的准备。以windows为例,创建一个win32窗口,假如只有一个主线程,那么主线程中有一个消息泵(也是主线程在维护),可以从这个线程中getmessage进行MessageLoop。这是win32的大概流程。

但是一个线程库把这些分离出来之后又怎么运转呢?这是我迫切想知道的问题。
谢谢大牛

lx3275852 2019-08-28
  • 打赏
  • 举报
回复
我都不知道你从哪看的资料,冒出这么多概念,我都不知道UI线程、工作线程,还去简单查了一下,好像是MFC的概念…… 我估计不能准确回答吧,但简单给你个指引。 1.实例A的对象,我先调用CreateWindow()生成一个窗口,如果根据资料所说,那么A对象所处在的线程就是UI线程,这样说对吗? 不对,对象=数据+函数,就是数据和函数打包起来,解决代码重用。线程是程序执行概念,和数据无关,只和函数有关。对象在线程中创建还是在主线程中创建,对消息队列处理没影响 2.虽然是用A对象的CreateWindow()创建的窗口,我能不能在run()函数中进行GetMessage()操作?(环境是windows)。 对,按你的描述,run才是线程函数,是线程的关键,你那个CreateWindow什么的和线程没关系,顶多是线程类里的一个普通函数,当然你可以在run里调用自己的CreateWindow 3.如果有一个线程在运行,它先调用A方法,A再调动B方法,B方法再调用C方法......A->B->C->D->E....,能不能认为凡是由线程启动调用的一系列方法都在一个线程内运行,也就是说不管下面有多少层调用,都是在该线程范围内的,是由该线程来执行的。 对,线程的主函数不管调用啥,它都在自己线程里 最后,给你一些指引 1.其实我觉得你没必要太纠结什么UI线程啊、工作线程的概念,你更应该关注线程概念的本身。我简单看了资料,个人理解是,可能它就是MFC里的两个线程类,微软的MFC提出的概念,不用MFC就没这些概念。这俩线程本身没太大区别,可能只有细节不同。工作线程应该也可以Createwindow啊、处理消息队列啥的,最多就是一些窗体细节上可能不如UI线程好。可能UI主要用了处理显示相关,就是和显卡、GPU啥的有联系,而工作线程只设计CPU吧。 2.线程,没有独享的资源,包括CPU、网、内存(数据)【这一点不像进程有独享资源】,全部资源尤其数据,都是进程内共享的。只不过CPU会分一部分时间片去跑你的线程代码。 3.Windows本身的窗口啊、消息处理那些都是单线程的。线程能解决的问题是,在某个子窗体消息处理时间很长或者死循环或阻塞时,其他消息会全部等待,这时整个程序界面都会无响应(所以窗体)。(1)如果这个子窗体的消息处理函数在线程中,那么只有这个子窗体会无响应,不会影响其他窗体。(2)如果只把某个消息处理的函数放在线程中,这个子窗体其他消息也不会无响应。
学习爱好者777 2019-08-17
  • 打赏
  • 举报
回复
引用 9 楼 lx3275852 的回复:
[quote=引用 8 楼 学习爱好者777 的回复:]
[quote=引用 7 楼 lx3275852 的回复:]
[quote=引用 6 楼 学习爱好者777 的回复:]
.....

我想向您再请教一个线程同步的问题,真是麻烦您了。。


[/quote]
你自己都说了线程栈了,从字面上理解,都知道当然是自己的,不共享……同一个函数,多个线程,每个线程单独一个线程栈资源,相互独立互不影响。多线程的共享指的是所在的栈之上的东西,比如全局变量、静态成员对象啊、它们共同的上一层栈的变量啊之类的,这些是共享的,会有冲突……[/quote]

大牛,又遇到一个问题想请教一下,是关于线程的。一般资料上写的线程有UI线程工作线程,解释说UI线程是能产生窗口的。

比如说,现在我创建一个类A,有两个成员函数,一个是创建窗口的,起名叫CreateWindow(),另外还有一个函数叫run()。

实例A的对象,我先调用CreateWindow()生成一个窗口,如果根据资料所说,那么A对象所处在的线程就是UI线程,这样说对吗?

2、虽然是用A对象的CreateWindow()创建的窗口,我能不能在run()函数中进行GetMessage()操作?(环境是windows)。

换句话说,创建窗口是一个函数,进行消息循环用另一个函数是否可以。我理解的依据是,这个线程就是UI线程,消息队列只和线程相关,不知是否正确?

3、线程的理解。
如果有一个线程在运行,它先调用A方法,A再调动B方法,B方法再调用C方法......A->B->C->D->E....,能不能认为凡是由线程启动调用的一系列方法都在一个线程内运行,也就是说不管下面有多少层调用,都是在该线程范围内的,是由该线程来执行的。

我问的问题都比较偏原理,不涉及具体业务,大牛在线的话给点提示,谢谢了。。。
Mr-Tree 2019-07-26
  • 打赏
  • 举报
回复
10万数据不会很高,100万的数据才20层,我自己试过30层的话数据量要达到10亿
学习爱好者777 2019-06-13
  • 打赏
  • 举报
回复
引用 9 楼 lx3275852 的回复:
[quote=引用 8 楼 学习爱好者777 的回复:]
[quote=引用 7 楼 lx3275852 的回复:]
[quote=引用 6 楼 学习爱好者777 的回复:]
.....

我想向您再请教一个线程同步的问题,真是麻烦您了。。
有两个线程访问同一个函数,比如函数g(),内部形式如下:
g(){
...
for(int i=0;i<1000;i++)
{
.....{执行代码} //此处被同步锁住
}
}
线程A和B都访问这个函数,资料上说的,每个线程都有自己的线程栈,能存放一些局部变量,大概是这个意思。
对于上面for循环里面的变量i,究竟是各有一份,还是共享呢?

假如第一种情况,各有一份

A进入,假如循环了100次时间片到了,对A来说此时i=100,B进入,如果它单独占有一份,那么它是从0开始?这个从直觉上感觉不对。认为它应该接着A累加的i开始,但是又说各有一份,不用互斥。所以这段我不理解了

这段代码,运行结果却是正确的,就是只对for循环内部的代码进行加锁就可以。我的问题是,对于函数内部的局部变量,各个线程是怎么处理的?


[/quote]
你自己都说了线程栈了,从字面上理解,都知道当然是自己的,不共享……同一个函数,多个线程,每个线程单独一个线程栈资源,相互独立互不影响。多线程的共享指的是所在的栈之上的东西,比如全局变量、静态成员对象啊、它们共同的上一层栈的变量啊之类的,这些是共享的,会有冲突……[/quote]

引用 9 楼 lx3275852 的回复:
[quote=引用 8 楼 学习爱好者777 的回复:]
[quote=引用 7 楼 lx3275852 的回复:]
[quote=引用 6 楼 学习爱好者777 的回复:]
.....

我想向您再请教一个线程同步的问题,真是麻烦您了。。
有两个线程访问同一个函数,比如函数g(),内部形式如下:
g(){
...
for(int i=0;i<1000;i++)
{
.....{执行代码} //此处被同步锁住
}
}
线程A和B都访问这个函数,资料上说的,每个线程都有自己的线程栈,能存放一些局部变量,大概是这个意思。
对于上面for循环里面的变量i,究竟是各有一份,还是共享呢?

假如第一种情况,各有一份

A进入,假如循环了100次时间片到了,对A来说此时i=100,B进入,如果它单独占有一份,那么它是从0开始?这个从直觉上感觉不对。认为它应该接着A累加的i开始,但是又说各有一份,不用互斥。所以这段我不理解了

这段代码,运行结果却是正确的,就是只对for循环内部的代码进行加锁就可以。我的问题是,对于函数内部的局部变量,各个线程是怎么处理的?


[/quote]
你自己都说了线程栈了,从字面上理解,都知道当然是自己的,不共享……同一个函数,多个线程,每个线程单独一个线程栈资源,相互独立互不影响。多线程的共享指的是所在的栈之上的东西,比如全局变量、静态成员对象啊、它们共同的上一层栈的变量啊之类的,这些是共享的,会有冲突……[/quote]
夜里看的太晚看迷糊了,确实是没问题的
lx3275852 2019-06-13
  • 打赏
  • 举报
回复
引用 8 楼 学习爱好者777 的回复:
[quote=引用 7 楼 lx3275852 的回复:] [quote=引用 6 楼 学习爱好者777 的回复:] .....
我想向您再请教一个线程同步的问题,真是麻烦您了。。 有两个线程访问同一个函数,比如函数g(),内部形式如下: g(){ ... for(int i=0;i<1000;i++) { .....{执行代码} //此处被同步锁住 } } 线程A和B都访问这个函数,资料上说的,每个线程都有自己的线程栈,能存放一些局部变量,大概是这个意思。 对于上面for循环里面的变量i,究竟是各有一份,还是共享呢? 假如第一种情况,各有一份 A进入,假如循环了100次时间片到了,对A来说此时i=100,B进入,如果它单独占有一份,那么它是从0开始?这个从直觉上感觉不对。认为它应该接着A累加的i开始,但是又说各有一份,不用互斥。所以这段我不理解了 这段代码,运行结果却是正确的,就是只对for循环内部的代码进行加锁就可以。我的问题是,对于函数内部的局部变量,各个线程是怎么处理的? [/quote] 你自己都说了线程栈了,从字面上理解,都知道当然是自己的,不共享……同一个函数,多个线程,每个线程单独一个线程栈资源,相互独立互不影响。多线程的共享指的是所在的栈之上的东西,比如全局变量、静态成员对象啊、它们共同的上一层栈的变量啊之类的,这些是共享的,会有冲突……
学习爱好者777 2019-05-31
  • 打赏
  • 举报
回复
引用 7 楼 lx3275852 的回复:
[quote=引用 6 楼 学习爱好者777 的回复:]
.....

我想向您再请教一个线程同步的问题,真是麻烦您了。。
有两个线程访问同一个函数,比如函数g(),内部形式如下:
g(){
...
for(int i=0;i<1000;i++)
{
.....{执行代码} //此处被同步锁住
}
}
线程A和B都访问这个函数,资料上说的,每个线程都有自己的线程栈,能存放一些局部变量,大概是这个意思。
对于上面for循环里面的变量i,究竟是各有一份,还是共享呢?

假如第一种情况,各有一份

A进入,假如循环了100次时间片到了,对A来说此时i=100,B进入,如果它单独占有一份,那么它是从0开始?这个从直觉上感觉不对。认为它应该接着A累加的i开始,但是又说各有一份,不用互斥。所以这段我不理解了

这段代码,运行结果却是正确的,就是只对for循环内部的代码进行加锁就可以。我的问题是,对于函数内部的局部变量,各个线程是怎么处理的?


lx3275852 2019-05-21
  • 打赏
  • 举报
回复
引用 6 楼 学习爱好者777 的回复:
我想再请教你一个问题,就是关于数据库连接的。

以Mysql为例,客户端连接服务器这块我认为是一个Socket连接。在代码编写的时候,就是创建连接,执行语句,得到结果集操作,然后关闭。

但是我看到有些代码会暂存这个连接,它的理由是:每次操作一个SQL就去连接数据库会非常消耗资源,比如创建socket连接等等。
我的问题是,假定客户端操作了一个SQL,然后隔了很久再去操作,这个连接还存在吗?

换句话说,就是到数据库连接到底是怎么回事儿? 这个连接有时间限长吗?要是您有这方面的研究请解惑一下。

你理解的没错。
1.连接问题是你理解的,Socket连接。
2.但不必每次操作都创建连接,查询完关闭连接,一般软件尤其后台,都是长连接的,创建一次,隔一会儿查一次隔一会儿查一次,直到服务关闭时才断开数据库连接。这样解决了创建连接消耗的资源
3.隔一段时间这个连接在不在,可以设置,myslq默认8小时没有操作自动断开,可以设置成永远不断开,也可以设置1分钟、5分钟断开,你可以自己百度怎么设置。
4.一般后台程序是忽略数据库设置的,做法是,创建一个连接的对象(或变量),查询时,判断连接是否断开,如果断开,就重新连接再查询;没断开的话就直接查询。
学习爱好者777 2019-05-12
  • 打赏
  • 举报
回复
引用 1 楼 lx3275852 的回复:
楼主你有点杞人忧天了,不会栈溢出。
首先,一个程序的栈,默认是1-2Mb,其实很大的。

我想再请教你一个问题,就是关于数据库连接的。

以Mysql为例,客户端连接服务器这块我认为是一个Socket连接。在代码编写的时候,就是创建连接,执行语句,得到结果集操作,然后关闭。

但是我看到有些代码会暂存这个连接,它的理由是:每次操作一个SQL就去连接数据库会非常消耗资源,比如创建socket连接等等。
我的问题是,假定客户端操作了一个SQL,然后隔了很久再去操作,这个连接还存在吗?

换句话说,就是到数据库连接到底是怎么回事儿? 这个连接有时间限长吗?要是您有这方面的研究请解惑一下。
学习爱好者777 2019-04-12
  • 打赏
  • 举报
回复
引用 1 楼 lx3275852 的回复:
楼主你有点杞人忧天了,不会栈溢出。
首先,一个程序的栈,默认是1-2Mb,其实很大的。
我把你的回复收录到自己写的小册子里面,提前给你说下。
学习爱好者777 2019-04-12
  • 打赏
  • 举报
回复
引用 1 楼 lx3275852 的回复:
楼主你有点杞人忧天了,不会栈溢出。
首先,一个程序的栈,默认是1-2Mb,其实很大的。
.....

谢谢你的回复,回答的非常好。后面第二个问题,你说的是“可以简单的理解它是二维数组储存的“,这一句能再说细一点吗?可以把概念大概说下,即使有些地方我不太懂可以自己去查下。
学习爱好者777 2019-04-12
  • 打赏
  • 举报
回复
引用 4 楼 lx3275852 的回复:
[quote=引用 2 楼 学习爱好者777 的回复:]
[quote=引用 1 楼 lx3275852 的回复:]
楼主你有点杞人忧天了,不会栈溢出。
首先,一个程序的栈,默认是1-2Mb,其实很大的。
.....

谢谢你的回复,回答的非常好。后面第二个问题,你说的是“可以简单的理解它是二维数组储存的“,这一句能再说细一点吗?可以把概念大概说下,即使有些地方我不太懂可以自己去查下。
[/quote]

储存这个东西,对于新手来说挺难理解的,不建议你问那么深,很难消化……简单的来说,反正数据库就是用数组来存,不是用二叉树来存的,二叉树只是索引(相当于目录)。
[/quote]
再次谢谢你,回复的太好了,你写的这段内容我要详细的读一下,我觉得你真的研究和思考了,非常感谢!要是有不问题的,我再来问你。
lx3275852 2019-04-12
  • 打赏
  • 举报
回复
引用 2 楼 学习爱好者777 的回复:
[quote=引用 1 楼 lx3275852 的回复:] 楼主你有点杞人忧天了,不会栈溢出。 首先,一个程序的栈,默认是1-2Mb,其实很大的。 .....
谢谢你的回复,回答的非常好。后面第二个问题,你说的是“可以简单的理解它是二维数组储存的“,这一句能再说细一点吗?可以把概念大概说下,即使有些地方我不太懂可以自己去查下。 [/quote] 储存这个东西,对于新手来说挺难理解的,不建议你问那么深,很难消化……简单的来说,反正数据库就是用数组来存,不是用二叉树来存的,二叉树只是索引(相当于目录)。 一般关系型数据库从大到小的逻辑储存单元分为:表空间,段,区,块(页),你可以把它看作是四维的数组。 其中最小的是块(页),一块(页)是4k-64k。是数据库读取的最小单元,是内存一次读取的内容,意思就是数据库从硬盘读到内存,最最最最少是一块(页),即使读一行的数据,也是直接把一页都读出来。详细你去参照内存的页的概念。块与块之间是物理连续的。 其次是区,也叫盘区,一个区一般是1M,就是多个页组成,多个区是物理连续的。 再接下来是段,一般一个段就是一个表,很多数据库的一个段就是一个文件,段与段之间是逻辑连续的,像内存中的链表差不多,物理可连续也可以不连续。 最后是表空间,一个段是一个文件的话,你可以理解为一个表空间就是一个文件夹。 重新梳理一下,一张表,看起来就是一张二维表,分别是行和列。它储存在硬盘里,储存形式和内存有点像,行像是 typedef struct line { char name[64]; int age; char info[256]; } LINE; 列像是 LINE line[5120];表示有5120行数据。其中如果数据比较多的话,有可能两行数据是一个块(页),512行数据组成一个区。那么这个二维表一共5120行数据,储存在10个区中,形成一个段(一个文件)。多个表最终储存成一个表空间(一个文件夹)。 然后你所谓的二叉树是单独的。一个表中,储存就像一个二维数组一样。索引像是一个目录,简单的二叉树索引,只是把某一行数据的位置存在二叉树中,不存实际数据(当然也有把整个一行数据放在二叉树索引中的)。就像指针一样 typedef struct Node { LINE *l; NODE *left; NODE *right; } NODE; 这个l记录的就是某一行的指针。它记录的可能就是line[333],一颗B+二叉树索引储存有点像这个结构。 非关系型数据库就不给你解释了,储存差不多也是分段啊、页啊之类的,但是比较复杂,它没有表的概念,换成了集合的概念,有空你自己区了解吧。
lx3275852 2019-04-08
  • 打赏
  • 举报
回复
楼主你有点杞人忧天了,不会栈溢出。 首先,一个程序的栈,默认是1-2Mb,其实很大的。 其次,二叉树数据的储存储存在堆中,不是栈中,而是在堆中。堆不止1-2Mb,1-2GB都没什么问题的。 再次,就拿1m的栈来说吧,程序栈每次调用一层,只占用很少的内存,就拿你那个insert为例,每多调一层的栈,只是多2个BinaryNode(2个对象引用,或指针),一个E e(两个对象引用或指针+一个data),一个int compareResult,以及一些函数调用必须的数据,加起来也才几十字节而已。就算每调用一层栈要1kb,整个栈能支撑你调用1000层的函数栈(即递归1000层)。 最后,1000层递归是什么概念??二叉树,树高度16,最多能放2^16=65536个(即64K),高度32最多能放2^32=4294967296(即4G),高度48能放2^48=2.81*10^14(即256TB)的数据,你硬盘爆了都装不下这么多,高度64能放2^64=1.84*10^19(即16Eb)的数据。这才64层递归……负责任的告诉你,高度300的二叉树足以把全宇宙所有原子都装进去,所以你压根都不需要担心栈会溢出…… 接下来简单回答你第二个问题,目前的关系型数据库的存储方式,不论有没有二叉树索引,你可以简单的理解成它是二维数组(或者说一个或多个一维数组)储存的。如果有二叉树索引的话,索引是单独储存,只是把一张表的关键字段用二叉树做索引。单独的二叉树索引你可以理解为给那个二维数组建了一个目录,用二叉树的方式让你迅速定位关键数据的位置,但没必要储存的时候也直接用二叉树来存硬盘。不知道说清楚没。

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧