使用缓存计算树结构中的总和

时间:2016-02-19 06:35:25

标签: algorithm caching recursion data-structures tree

我需要知道什么类型的算法在缓存机制的帮助下有效地计算树结构中的总和。

我想解决的问题很简单:有两种类型的节点:文件夹(F)和文件(L)......看起来像文件系统:) ...

可以在树结构中创建和移动文件夹和文件。文件大小不断变化。

现在我想要一个返回节点内容大小的函数size(node)

样品:

F1
 - L1 : 10
 - L2 : 33
F2 
 - F21
    - F211
         - L3 : 2
         - L4 : 8
    - F212
         - F2121
               - L5 : 18
         - F2122
               - L6 : 21
               - L7 : 3            

size(node) gives:

size(F1):43
size(F2):52
size(F21):52
size(F211):10
size(F212):42
size(L5):18
...

已知的解决方案是递归加起来:) ...

size(node) 
  int res=0;
  if(node is File)
    return sizeOfFile(node)
  else
    for each child of node
      res += size(child)
    end 
  end
  return res
end

我问自己,我可以在这里使用某种类型的缓存。我需要搜索的算法的名称和类型是什么?

2 个答案:

答案 0 :(得分:1)

你可以做一件事。您可以在访问节点并存储它时计算成本。认为这就像手机或电脑上的存储。这里的例子会更好。enter image description here

我没有在这里给任何节点付出代价。假设您访问节点编号4.您通过添加成本6和7来计算成本并将其存储在数据库中。在数据库上,这可以像directory path -- cost一样存储。现在假设您要计算成本2.您不需要访问4,因为您已经预先计算了成本。您只需要计算5的成本,然后就完成了。因此,我们将数据库中的成本5和2存储起来。下次如果你想知道4或5的大小,你可以从数据库中获取它。

我假设这是一个文件结构。所以你需要更频繁地去目录。您访问的越多,将完成更多的尺寸存储。计算成本越容易。最后,如果您更改文件或移动它们,只需计算该节点的成本。

我认为这里的优点是每次访问节点时都不需要计算成本。你将使用一个存储空间,随着时间的推移,你可以更快地计算尺寸。

希望它有意义。

答案 1 :(得分:0)

附加了使用swift和核心数据的实现。它的作用是在核心数据/ sqlite数据库中生成随机树数据,并将一些节点(文件夹)移动到其他目标文件夹中。输出看起来像这样:

Building the tree ...
insert file nodes: [0(15): 1  100]
insert file nodes: [1(15): 101  200]
insert file nodes: [2(15): 201  300]
insert file nodes: [3(15): 301  400]
insert file nodes: [4(15): 401  500]
insert file nodes: [5(15): 501  600]
insert file nodes: [6(15): 601  700]
insert file nodes: [7(15): 701  800]
insert file nodes: [8(15): 801  900]
insert file nodes: [9(15): 901  1000]
insert file nodes: [10(15): 1001  1100]
insert file nodes: [11(15): 1101  1200]
insert file nodes: [12(15): 1201  1300]
insert file nodes: [13(15): 1301  1400]
insert file nodes: [14(15): 1401  1500]
Congratulations! Tree exists. It's initial size is:75563
Moving F000120 to target folder F000103 ...
before moving:
size[F000120] = 607 path = /F000001/F000003/
source folder: size[F000003] = 37388
target folder: size[F000103] = 1134
after moving:
size[F000120] = 607 path = /F000001/F000003/F000014/F000030/F000072/F000103/
source folder: size[F000003] = 37995
target folder: size[F000103] = 1741
Moving F000200 to target folder F000209 ...
before moving:
size[F000200] = 224 path = /F000001/F000016/
source folder: size[F000016] = 1686
target folder: size[F000209] = 215
after moving:
size[F000200] = 224 path = /F000001/F000002/F000006/F000007/F000041/F000209/
source folder: size[F000016] = 1462
target folder: size[F000209] = 439
Moving the nodes completed. Tree size is:75563

核心数据模型是:

enter image description here

AppDelegate

//
//  AppDelegate.swift
//  SizesNTrees
//
//  Created by Markus Schmid on 20.02.16.
//

import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    // MARK: - Core Data stack

    lazy var applicationDocumentsDirectory: NSURL = {
        let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
        return urls[urls.count-1]
    }()

    lazy var managedObjectModel: NSManagedObjectModel = {
        let modelURL = NSBundle.mainBundle().URLForResource("SizesNTrees", withExtension: "momd")!
        return NSManagedObjectModel(contentsOfURL: modelURL)!
    }()

    lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SizesNTrees.sqlite")
        var failureReason = "There was an error creating or loading the application's saved data."
        do {
            try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
        } catch {
            var dict = [String: AnyObject]()
            dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
            dict[NSLocalizedFailureReasonErrorKey] = failureReason

            dict[NSUnderlyingErrorKey] = error as NSError
            let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
            NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
            abort()
        }

        return coordinator
    }()

    lazy var managedObjectContext: NSManagedObjectContext = {
        let coordinator = self.persistentStoreCoordinator
        var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
        managedObjectContext.persistentStoreCoordinator = coordinator
        return managedObjectContext
    }()

    // MARK: - Core Data Saving support

    func saveContext () {
        if managedObjectContext.hasChanges {
            do {
                try managedObjectContext.save()
            } catch {
                let nserror = error as NSError
                NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
                abort()
            }
        }
    }

}

ViewController

//
//  ViewController.swift
//  SizesNTrees
//
//  Created by Markus Schmid on 20.02.16.
//

import UIKit
import CoreData

enum NodeType: Int {
    case File
    case Folder
}

var node : Node!

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        if let root=nodeWithName("F000001") {
            print("Tree exists. Initial tree size is:\(root.size!)")
        }
        else {
            print("Building the tree ...")
            buildTree(300, noFiles:1500)
        }
        if let root=nodeWithName("F000001") {
            print("Congratulations! Tree exists. It's initial size is:\(root.size!)")
            moveNode("F000120",targetFolderName:"F000103")
            moveNode("F000200",targetFolderName:"F000209")
            print("Moving the nodes completed. Tree size is:\(root.size!)")
        }
        else {
            print("Tree is missing.")
            return
        }
        do {
            let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
            let managedContext = appDelegate.managedObjectContext
            try managedContext.save()
        } catch let error as NSError {
            print("Could not save \(error), \(error.userInfo)")
        }

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func moveNode(nodeName:String, targetFolderName:String) {
        // try to move move nodeName to targetFolderName
        if let node=nodeWithName(nodeName) {
            if let targetFolder=nodeWithName(targetFolderName) {
                print ("Moving \(nodeName) to target folder \(targetFolderName) ... ")
                print("before moving:")
                print("   size[\(node.name!)] = \(node.size!) path = \(path(node))")
                let origParent=node.parent
                print("   source folder: size[\(origParent!.name!)] = \(origParent!.size!)")
                print("   target folder: size[\(targetFolder.name!)] = \(targetFolder.size!)")
                if !isDescendantOrSelf(targetFolder, ascendant: node) {
                    move(node, toFolder: targetFolder)
                    print("after moving:")
                    print("   size[\(node.name!)] = \(node.size!) path = \(path(node))")
                    print("   source folder: size[\(origParent!.name!)] = \(origParent!.size!)")
                    print("   target folder: size[\(targetFolder.name!)] = \(targetFolder.size!)")
                }
                else {
                    print ("Moving folder to target folder failed! Target folder is descendant from folder.")
                }
            }
            else {
                print ("Target folder \(targetFolderName) not found!")
            }
        }
        else {
            print ("Node \(nodeName) not found!")
        }
    }

    func buildTree(noFolders:Int,noFiles:Int) {
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let managedContext = appDelegate.managedObjectContext
        let nodeEntity =  NSEntityDescription.entityForName("Node", inManagedObjectContext:managedContext)
        // create folders
        autoreleasepool {
            for index in 1...noFolders {
                node = NSManagedObject(entity: nodeEntity!, insertIntoManagedObjectContext: managedContext) as! Node
                node.name=String(format: "F%06d", index)
                node.size=0
                node.type=NodeType.Folder.rawValue
                node.nr=index
                if index>1 {
                    node.parent=nodeOfType(NodeType.Folder.rawValue, nr:Int(arc4random_uniform(UInt32(index-1))+1))
                }
            }
            do {
                try managedContext.save()
            } catch let error as NSError {
                print("Could not save \(error), \(error.userInfo)")
            }
            managedContext.reset()
        }
        // create files
        let bSize = 100
        let noB = noFiles/bSize
        var index = 0
        for var bNr=0;(bNr<noB || ((bNr*bSize<noFiles) && (((bNr+1)*bSize)>noFiles)) );bNr++ {
            autoreleasepool {
                var startIndex=0
                var maxIndex=0
                if bNr<noB {
                    startIndex=bNr*bSize+1
                    maxIndex=(bNr+1)*bSize
                }
                else {
                    startIndex=bNr*bSize+1
                    maxIndex=noFiles
                }
                print("insert file nodes: [\(bNr)(\(noB)): \(startIndex)  \(maxIndex)]")
                for index=startIndex;index<maxIndex+1;index++ {
                    let node = NSManagedObject(entity: nodeEntity!, insertIntoManagedObjectContext: managedContext) as! Node
                    node.name=String(format: "L%06d", index)
                    node.size=Int(arc4random_uniform(100)+1)
                    node.type=NodeType.File.rawValue
                    node.nr=index
                    node.parent=self.nodeOfType(NodeType.Folder.rawValue, nr:Int(arc4random_uniform(UInt32(noFolders))+1))
                }
                do {
                    try managedContext.save()
                } catch let error as NSError {
                    print("Could not save \(error), \(error.userInfo)")
                }
                managedContext.reset()
            }
        }

        fillSize();
    }

    func nodeWithName(name:String, context:NSManagedObjectContext) -> Node? {
        var node : Node?
        let managedContext = context
        let predicate = NSPredicate(format:"name = %@", name)
        let request = NSFetchRequest(entityName:"Node")
        request.predicate=predicate

        do {
            let fetchedNodes = try managedContext.executeFetchRequest(request) as! [Node]
            if fetchedNodes.count>0 {
                node=fetchedNodes[0]
            }
            else {
                node=nil
            }
        } catch {
            fatalError("Failed to fetch nodes: \(error)")
        }
        return node
    }

    func nodeOfType(type:NSNumber, nr:NSNumber, context:NSManagedObjectContext) -> Node? {
        var node : Node?
        let managedContext = context
        let predicate = NSPredicate(format:"type = %@ AND nr = %@", type, nr)
        let request = NSFetchRequest(entityName:"Node")
        request.predicate=predicate

        do {
            let fetchedNodes = try managedContext.executeFetchRequest(request) as! [Node]
            if fetchedNodes.count>0 {
                node=fetchedNodes[0]
            }
            else {
                node=nil
            }
        } catch {
            fatalError("Failed to fetch nodes: \(error)")
        }
        return node
    }

    func nodeWithName(name:String) -> Node? {
        var node : Node?
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let managedContext = appDelegate.managedObjectContext
        let predicate = NSPredicate(format:"name = %@", name)
        let request = NSFetchRequest(entityName:"Node")
        request.predicate=predicate

        do {
            let fetchedNodes = try managedContext.executeFetchRequest(request) as! [Node]
            if fetchedNodes.count>0 {
                node=fetchedNodes[0]
            }
            else {
                node=nil
            }
        } catch {
            fatalError("Failed to fetch nodes: \(error)")
        }
        return node
    }


    func nodeOfType(type:NSNumber, nr:NSNumber) -> Node? {
        var node : Node?
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let managedContext = appDelegate.managedObjectContext
        let predicate = NSPredicate(format:"type = %@ AND nr = %@", type, nr)
        let request = NSFetchRequest(entityName:"Node")
        request.predicate=predicate

        do {
            let fetchedNodes = try managedContext.executeFetchRequest(request) as! [Node]
            if fetchedNodes.count>0 {
                node=fetchedNodes[0]
            }
            else {
                node=nil
            }
        } catch {
            fatalError("Failed to fetch nodes: \(error)")
        }
        return node
    }

    func fillSize() {
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let managedContext = appDelegate.managedObjectContext
        let predicate = NSPredicate(format:"type = 1")
        let request = NSFetchRequest(entityName:"Node")
        request.predicate=predicate
        do {
            let fetchedNodes = try managedContext.executeFetchRequest(request) as! [Node]
            for node in fetchedNodes {
                node.size=size(node);
            }
        } catch {
            fatalError("Failed to fetch nodes: \(error)")
        }
        do {
            try managedContext.save()
        } catch let error as NSError {
            print("Could not save \(error), \(error.userInfo)")
        }
    }

    func size(node:Node) -> Int {
        var res : Int = 0
        if node.type == 0 {
            res=(node.size?.integerValue)!;
        }
        else {
            if let nodeChilds=node.childs {
                for child in nodeChilds {
                    res = res + size(child);
                }
            }
        }
        return res;
    }

    func path(node:Node) -> String {
        if let parent=node.parent {
            return path(parent).stringByAppendingString((node.parent?.name)!).stringByAppendingString("/")
        }
        else {
            return "/"
        }
    }

    func move(node:Node, toFolder:Node) -> Bool {
        var success=false
        if let size=node.size {
            if let parent=node.parent {
                if parent.isEqual(toFolder) {
                    return true
                }
                else {
                    propagateSizeChange(-size.integerValue, parent: parent)
                }
            }
            propagateSizeChange(size.integerValue, parent: toFolder)
            node.parent=toFolder
            success=true
        }
        return success
    }

    func setSizeFor(file:Node, var newSize:Int) {
        if newSize<=0 {
            newSize=0
        }
        if let size=node.size {
            if let parent=node.parent {
                propagateSizeChange(newSize-size.integerValue, parent: parent)
            }
        }
    }

    func isDescendantOrSelf(node:Node, ascendant:Node) -> Bool {
        var success=false
        if ascendant.isEqual(node) {
            return true
        }
        else {
            if let nodeChilds=ascendant.childs {
                if nodeChilds.contains(node) {
                    return true;
                }
                else {
                    for child in nodeChilds {
                        success = (success || self.isDescendantOrSelf(node,ascendant:child))
                        if success {
                            break;
                        }
                    }
                }
            }
        }
        return success
    }

    func propagateSizeChange(value:Int, parent:Node) {
        if parent.type == 1 {
            parent.size = NSNumber(integer:(parent.size!.integerValue + value))
            if let parent=parent.parent {
                parent.size = NSNumber(integer:(parent.size!.integerValue + value))
                propagateSizeChange(value, parent: parent)
            }
        }
    }
}